首页 > 解决方案 > 如何使用 LiveData 正确更新 Android 的 RecyclerView?

问题描述

底线问题

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

似乎只有在 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 列表中更新)。我怎样才能正确地做到这一点?

添加/删除文章

如果您从基础集合中添加/删除项目,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 中的完全刷新?

标签: androidandroid-recyclerviewandroid-livedata

解决方案


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

看看LiveAdapter

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

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

并将适配器与您的 RecyclerView 绑定

// Kotlin sample
LiveAdapter(
        data = liveListOfItems,
        lifecycleOwner = this@MainActivity,
        variable = BR.item )
       .map<Header, ItemHeaderBinding>(R.layout.item_header) {
           onBind{

           }
           onClick{

           }
           areContentsTheSame { old: Header, new: Header ->
               return@areContentsTheSame old.text == new.text
           }
           areItemSame { old: Header, new: Header ->
               return@areContentsTheSame old.text == new.text
           }
       }
       .map<Point, ItemPointBinding>(R.layout.item_point) {
           onBind{

           }
           onClick{

           }
           areContentsTheSame { old: Point, new: Point ->
               return@areContentsTheSame old.id == new.id
           }
           areItemSame { old: Header, new: Header ->
               return@areContentsTheSame old.text == new.text
           }
       }
       .into(recyclerview)

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


推荐阅读