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

Ed *_*ave 7 android android-recyclerview android-livedata

底线问题

如果我使用MutableLiveData<List<Article>>,当文章的标题/内容更改,添加了新的文章以及删除了文章时,是否有办法正确通知观察者?

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

假设的例子

假设以下...

我的LiveData类看起来像这样:

public class ArticleViewModel extends ViewModel {

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

}
Run Code Online (Sandbox Code Playgroud)

我想使用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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

添加/删除文章

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

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

那不是真的很低效吗,因为它会触发RecyclerView.Adapter中的完全刷新,而不是仅仅添加/删除单个行?

更换名字

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

再次,这不是真的效率不高吗,因为它会触发RecyclerView.Adapter中的完全刷新吗?

Ros*_*mar 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)
}
}
Run Code Online (Sandbox Code Playgroud)

这里需要注意两点:

1.首先,我们的值是一个实现 BaseObservable 接口的泛型类型。这使我们能够访问 OnPropertyChangedCallback。

2.接下来,每当 BaseObservable 的某些属性发生变化时,我们只需将顶层 value 属性重置为其当前值,从而提醒 LiveData 的观察者某些内容发生了变化。