ListAdapter DiffUtils newItem 和 oldItem 在调用 SubmitList() 时相同

问题描述 投票:0回答:3

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

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

我偶然发现了这篇文章: ListAdapter 未更新 RecyclerView 中的项目

但这里的区别在于,事实上,DiffUtils 被调用,但不知何故

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

我在这里留下了相关的片段和我创建的测试项目的链接,这样我就可以封装行为并将其与其他所有内容分开进行测试。

片段 - 只需调用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

干杯!

android kotlin android-recyclerview listadapter android-diffutils
3个回答
3
投票

不要将可变数据类或可变列表与 DiffUtil 一起使用。它可能会导致各种问题。 DiffUtil 依赖于比较两个列表,因此如果其中一个列表是可变的并且已更改,则它无法成功比较新旧列表,因为没有先前状态的记录。

我没有花时间缩小你的确切问题范围,但我敢打赌,如果你将 Repo 的

getItems()
更改为 return
items.toList()
(因此改变 Repo 不会改变下游列表),并将 SimpleItem 更改为不可变的类,你的问题就会消失。

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

如果您的存储库返回一个列表流,当向其报告更改时会自动发出列表流,那么它会更干净。那么你的 ViewModel 就不必既报告更改,又记得再次手动查询列表状态。

我会使用

toList()
而不是
toMutableList()
。可变列表表明您计划更改列表而不是仅仅读取它,而您绝不能在将列表传递给 DiffUtil 时执行此操作。


1
投票

itemClickCount
属性声明为
val
,并从 Repo 对象中获取作为不可变列表的列表,就达到了 Tenfour04 建议的效果。

作为附加观察,如果我将

itemClickCount
属性保留为
var
但完全替换该元素并重新提交更新后的列表,则它可以正常工作。所以问题似乎是直接在 Repo 列表中修改对象的可变属性。在这种情况下,在
.toList()
中使用
getList()
没有帮助。


0
投票

当我调试应用程序时,我看到数据类的 equals 函数在 areContentsTheSame 函数之后被调用。

如果数据类没有重写 equals 方法,oldItem 和 newItem 将始终相同。

要解决这个问题,只需重写数据类中的 equals 函数即可。

override fun equals(other: Any?): Boolean {
    return super.equals(other)
}
© www.soinside.com 2019 - 2024. All rights reserved.