如何使用LiveData正确更新Android的RecyclerView?

时间:2018-05-31 17:04:27

标签: android android-recyclerview android-livedata

底线问题

如果我使用MutableLiveData<List<Article>>,是否有办法在文章的标题/内容发生变化时正确通知观察者,是否添加了新文章,并删除了文章?< / p>

似乎只有在LiveData上设置一个全新的集合时才会发出通知,这似乎会导致UI刷新效率非常低。

假设示例

假设以下......

我的LiveData类看起来像这样:

public class ArticleViewModel extends ViewModel {

    private final MutableLiveData<List<Article>> mArticles = new MutableLiveData<>();

}

我想使用RecyclerView在列表中显示文章。因此,只要我的Fragment观察到ArticleViewModel的LiveData中的更改,它就会在我的自定义RecyclerView.Adapter类上调用以下方法:

public class ArticleRecyclerViewAdapater extends RecyclerView.Adapter<Article> {

    private final ArrayList<Article> mValues = new ArrayList<>();


    public void resetValues(Collection<Article> articles) {
        mValues.clear();
        mValues.addAll(articles);
        notifyDataSetChanged();
    }
}

最后,我的应用程序将允许用户添加新文章,删除现有文章,以及更改现有文章的名称(需要在RecyclerView列表中更新)。我怎么能这样做呢?

添加/删除文章

如果您从基础Collection添加/删除项目,LiveData构造似乎不会通知观察者。看来你必须调用LiveData#setValue,也许ArticleViewModel需要一个看起来像这样的方法:

public void deleteArticle(int index) {
    final List<Article> articles = mArticles.getValue();
    articles.remove(index);
    mArticles.setValue(articles);
}

这不是非常低效,因为它会在RecyclerView.Adapter中触发完全刷新,而不是仅添加/删除单行吗?

更改名称

如果更改基础集合中项目的内容,LiveData构造似乎不会通知观察者。因此,如果我想更改现有文章的标题并将其反映在RecyclerView中,那么我的ArticleViewModel必须修改该对象并使用整个集合调用LiveData#setValue。

同样,这不是非常低效,因为它会在RecyclerView.Adapter中触发完全刷新吗?

3 个答案:

答案 0 :(得分:0)

案例1:

添加或删除时 因此,当您在列表中添加或删除元素时,您不会更改列表项的引用,因此,每次修改liveData项目时,都必须通过调用setValue方法来更新实时数据值(如果要在主线程上更新项目) )或发布值(在后台线程上更新值时)

问题是效率不高

解决方案 使用diffutil

情况2:通过编辑项目更新项目属性时。

问题

LiveData仅在顶级值发生更改时才向其观察者发出更改的警报。但是,对于复杂的对象,这仅意味着对象引用已更改,而对象的某些属性已更改则不会。

解决方案

要观察属性的变化,您需要PropertyAwareMutableLiveData

class PropertyAwareMutableLiveData<T: BaseObservable>: MutableLiveData<T>() {
private val callback = object: Observable.OnPropertyChangedCallback() {
    override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
        value = value
    }
}
override fun setValue(value: T?) {
    super.setValue(value)

    value?.addOnPropertyChangedCallback(callback)
}
}

这里要注意两件事:

1。首先,我们的值是实现BaseObservable接口的泛型类型。这使我们可以访问OnPropertyChangedCallback。

2。接下来,每当BaseObservable的某些属性发生更改时,我们只需将顶级值属性重置为其当前值,从而向LiveData的观察者发出警报,通知某些更改。

答案 1 :(得分:0)

LiveData仅在其包装对象引用发生更改时通知。当您将新的List分配给LiveData时,它将通知,因为其包装对象引用已更改,但是如果从LiveData的{​​{1}}添加/删除项目,它将不通知,因为它仍然具有与包装对象相同的List引用。因此,您可以通过扩展List来克服此问题,如另一个栈溢出问题中的here所述。

答案 2 :(得分:0)

我知道现在回答已经太晚了。 但我希望它能帮助其他开发者寻找类似问题的解决方案。

看看LiveAdapter

您只需要在 Gradle 中添加最新的依赖项即可。

var getNames = function() {

  var names = ["Aled Ratcliffe",
    "Cassie Gibbons", "Ephraim Holman", "Asim Ayala", "Johnny Villegas", "Kanye Bond", "Leoni Akhtar", "Duane Velasquez", "Elana Stark", "John Smith"
  ];

  return names;
}

var fullNames = getNames();

var searchFirstNames = function(namesAr, nameQuery) {
  var nameToQuery = nameQuery;
  var matches = namesAr.filter(filterFirstNames);
  return matches;
}

function filterFirstNames(namesArr) {
  let nameObj = {};
  const nameParts = namesArr.split(" ");
  nameObj.fName = nameParts[0];
  nameObj.lName = nameParts[1];
  let result = false;
  if (namesArr.fName === nameToQuery) {
    result = true;
  }
  return result;
}

var matchingFirstNames = searchFirstNames(fullNames, "John");

并将适配器与您的 RecyclerView 绑定

dependencies {
    implementation 'com.github.RaviKoradiya:LiveAdapter:1.3.4'
    // kapt 'com.android.databinding:compiler:GRADLE_PLUGIN_VERSION' // this line only for Kotlin projects
}

就是这样。不需要为适配器实现编写额外的代码,观察 LiveData 并通知适配器。