Android数据绑定ObservableList行为问题

时间:2017-04-06 08:33:43

标签: android android-recyclerview android-databinding observablelist android-binding-adapter

我发现很难发现android.databinding.ObservableList作为数据绑定功能的真正存在理由。

首先,通过data binding通过xml将列表添加到RecyclerView,它看起来像是一个很酷的工具来显示列表。 为此,我做了BindingAdapter这样:

@BindingAdapter(value = {"items"}, requireAll = false)
public static void setMyAdapterItems(RecyclerView view, ObservableList <T> items) {
    if(items != null && (view.getAdapter() instanceof MyAdapter)) {
        ((GenericAdapter<T>) view.getAdapter()).setItems(items);
    }
}

这样,我可以在设置了app:items的{​​{1}}中使用属性RecyclerView来更新其项目。

现在MyAdapter的最佳功能是你可以添加ObservableList,它可以处理OnListChangedCallback中可用的相同事件,以添加/移动/删除/更改其中的项目没有实际重新加载整个列表。

所以我认为实施的逻辑是休假:

  1. 我从空RecyclerView
  2. 开始
  3. 当我从我的API中提取项目时,我实例化MyAdapter包装它们并将其传递给ObservableArrayList
  4. 数据绑定会调用我的binding将项目传递给BindingAdapter
  5. MyAdapter收到新项目时,会清除旧项目,并为收到的MyAdapter添加OnListChangedCallback以处理微更改
  6. 如果ObservableList发生任何变化,ObservableList会相应更改而不会完全刷新
  7. 如果我想要显示完全不同的相同项类型集,我可以重新设置MyAdapter变量,以便再次调用bindingBindingAdapter项将完全改变。
  8. 例如,如果我想要显示MyAdapter类型的项目,我有两个不同的列表:“拥有游戏”和“愿望清单游戏”,我可以调用Game来完全刷新显示的项目,但是,例如,如果我在列表周围移动“愿望清单游戏”以按相关性组织它们,则只会在每个列表中执行微更改而不刷新整个事物。

    事实证明这个想法是不可行的,因为每次对binding.setItems(whateverItems)进行一次更改时,数据绑定都会重新执行BindingAdapter,所以例如我观察到了以下行为:

    1. 我从空ObservableList
    2. 开始
    3. 当我从我的API中提取项目时,我实例化MyAdapter包装它们并将其传递给ObservableArrayList
    4. 数据绑定会调用我的binding将项目传递给BindingAdapter
    5. MyAdapter收到新项目时,会清除旧项目,并为收到的MyAdapter添加OnListChangedCallback以处理微更改
    6. 如果ObservableList发生任何变化,则会再次调用ObservableList,因此BindingAdapter会再次收到整个列表并完全刷新。
    7. 这种行为对我来说似乎很不妥,因为阻止MyAdapter在数据绑定ObservableList中可用。我无法认真地找出这种行为是合情合理的案例。

      我查了一些例子:herethis other SO question

      在第一个链接中,所有示例都将xml直接用于ObservableList,甚至没有传递形式Adapter和实际数据绑定,而在SO答案中链接的代码中,开发人员基本上做了我尝试做的事情,添加:

      xml

      在他if (this.items == items){ return; } 的开头,放弃因Adapter.setItems(ObservableList<T> items)中的简单更改而调用该方法的所有情况。

      这种行为有什么需要?可能在某些情况下这种行为是可取的?我觉得ObservableList是一个添加了数据绑定的功能,除非与实际数据绑定一起使用时非常有用,在这种情况下它会强制您防范其行为。 如果我在ObservableList数据标记和List签名中将其声明为简单xml,那么我可以将其转发回BindingAdapter内的ObservableList工作得很好,但这是一个非常糟糕的黑客。 如果它只是数据绑定的一个单独功能,在每次更改时都不会触发绑定,那么在我看来它会好得多。

1 个答案:

答案 0 :(得分:1)

根据文档https://developer.android.com/topic/libraries/data-binding/index.html#observable_collections中提供的示例,ObservableList用于使用键整数访问它的项目,即:

<data>
    <import type="android.databinding.ObservableList"/>
    <import type="com.example.my.app.Fields"/>
    <variable name="user" type="ObservableList&lt;Object&gt;"/>
</data>
…
<TextView
   android:text='@{user[Fields.LAST_NAME]}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

因此,当在ObservableList内部更改某些内容时,它会触发BindingAdapter来更新UI。我认为这是当前使用ObservableList而DataBinding处于开发状态时的主要目的。也许在将来,DataBinding将使用新的SomeObservableList进行更新,该if (this.items == items){return;}将用于RecyclerView。 同时,如果ObservableList适用于您,您可以使用 $inherited1 = '1'; $inherited2 = '2'; $example = function($arg) use (&$inherited1, &$inherited2){ // access the $inherited1 and $inherited2 variables directly var_dump($arg . $inherited1 . $inherited2); } $example('arg'); ,或者重新考虑使用<script> window.result = [ { "title": "What You Need To Know About CSS Variables", }, { "title": "Freebie: 4 Great Looking Pricing Tables", }, { "title": "20 Interesting JavaScript and CSS Libraries for February 2016", }, { "title": "Quick Tip: The Easiest Way To Make Responsive Headers", }, { "title": "Learn SQL In 20 Minutes", }, { "title": "Creating Your First Desktop App With HTML, JS and Electron", } ]; </script> <form id="main" v-cloak> <div class="bar"> <!-- Create a binding between the searchString model and the text field --> <input type="text" v-model="searchString" placeholder="Enter your search terms" /> </div> <ul> <!-- Render a li element for every entry in the computed filteredArticles array. --> <li v-for="article in filteredArticles"> <p>{{article.title}}</p> </li> </ul> </form> <script> window.result(2) = [ { "title": "What You Need To Know About CSS Variables", } ]; </script> <form id="main2"> <div class="bar"> <!-- Create a binding between the searchString model and the text field --> <input type="text" v-model="searchString" placeholder="Enter your search terms" /> </div> <ul> <!-- Render a li element for every entry in the computed filteredArticles array. --> <li v-for="article in filteredArticles"> <p>{{article.title}}</p> </li> </ul> </form> <script> window.result(3) = [ { "title": "What You Need To Know About CSS Variables", } ]; </script> <form id="main3"> <div class="bar"> <!-- Create a binding between the searchString model and the text field --> <input type="text" v-model="searchString" placeholder="Enter your search terms" /> </div> <ul> <!-- Render a li element for every entry in the computed filteredArticles array. --> <li v-for="article in filteredArticles"> <p>{{article.title}}</p> </li> </ul> </form> var results = window.result; var demo = new Vue({ el: '#main', data: { searchString: "", articles:results }, computed: { // A computed property that holds only those articles that match the searchString. filteredArticles: function () { var articles_array = this.articles, searchString = this.searchString; if(!searchString){ return articles_array; } searchString = searchString.trim().toLowerCase(); articles_array = articles_array.filter(function(item){ if(item.title.toLowerCase().indexOf(searchString) !== -1){ return item; } }) return articles_array;; } } 的逻辑。