调用 submitList() 时,ListAdapter DiffUtils newItem 和 oldItem 相同

ListAdapter DiffUtils newItem and oldItem the same when submitList() called

仅供参考,我并不是在寻找 'fix',而是在寻找可能有助于更多地理解这些看似愚蠢的事情是如何工作的解释和讨论。

当我意识到某个地方的某个列表没有正确更新时,我正在从事这个更大的项目。仔细观察,项目被正确修改,如果您 'scrolled away' 并返回,项目的信息将正确显示。

我偶然发现了这篇文章:

但这里的不同之处在于,事实上,DiffUtils 被调用了,但不知何故 newItemoldItem 是一样的!我知道库 假设您正在使用 Room 或任何其他 ORM,每次更新时都会提供一个新的异步列表 ,但事情就是这样。如果我“天真地”提交列表,甚至不会调用 DiffUtils。但是,如果我像某些人建议的那样将列表提交为 list.toMutableList(),则调用 DiffUtils,但不知何故,新旧项目已经相同,因此,那一刻没有任何更新(通过放置断点验证了这一点里面 areContentsTheSame).

我将相关片段和 link 留给您我创建的测试项目,这样我就可以封装行为并将其与其他所有内容分开测试。

Fragment - 只是调用 submitList

viewModel.items.observe(viewLifecycleOwner) {
    adapter.submitList(it.toMutableList())
}

视图模型

private val _items = MutableLiveData<List<SimpleItem>>()
val items: LiveData<List<SimpleItem>>
    get() = _items

init {
    _items.value = ItemsRepo.getItems()
}

fun onItemClick(itemId: Int) {
    ItemsRepo.addItemCount(itemId)
    _items.value = ItemsRepo.getItems()
}

我创建一些数据的“Repo” 对象 ItemsRepo {

private var items = mutableListOf(
    SimpleItem(1),
    SimpleItem(2),
    SimpleItem(3),
    SimpleItem(4),
    SimpleItem(5)
)

fun getItems(): List<SimpleItem> {
    return items
}

fun addItemCount(itemId: Int) {
    items.find { it.itemId == itemId }?.let {
        it.itemClickCount += 1
    }
}

GitHub 回购: https://github.com/ellasaro/ListAdapterTest

干杯!

不要在 DiffUtil 中使用可变数据 classes 或可变列表。它会导致各种问题。 DiffUtil 依赖于比较两个列表,所以如果其中一个是可变的并且已经被改变,它就不能成功地比较新旧,因为没有之前状态的记录。

我没有花时间缩小你的确切问题范围,但我敢打赌,如果你将回购协议的 getItems() 更改为 return items.toList()(因此改变回购协议不会t 改变下游列表),并将 SimpleItem 更改为不可变的 class,您的问题就会消失。

不幸的是,使 SimpleItem 不可变会有点麻烦。点击侦听器而不是改变项目将必须向 repo 报告已更改项目的 id,repo 必须手动将其换掉,然后您刷新列表。

如果你的 Repo return 是一个列表流,当向它报告更改时自动发出列表流,它会更干净。然后您的 ViewModel 就不必既报告更改又记得再次手动查询列表状态。

我会使用 toList() 而不是 toMutableList()。可变列表表明您计划改变列表而不是仅仅读取它,对于传递给 DiffUtil 的列表,您绝不能这样做。

itemClickCount 属性 声明为 val,并将列表作为不可变列表从 Repo 对象中获取,正如 Tenfour04 所建议的那样。

作为补充观察,如果将 itemClickCount 属性 保留为 var 但完全替换元素并 re-submit 更新列表,它可以正常工作。所以问题似乎是 直接在 Repo 的列表 中修改对象的可变 属性。在 getList() 中使用 .toList() 在这种情况下没有帮助。