Android DiffUtil.ItemCallback areContentsTheSame 不同的ID
Android DiffUtil.ItemCallback areContentsTheSame with different IDs
我正在使用 RecyclerView
来显示从数据库中检索为 Java 对象的项目列表。对象中的字段之一是数据库中项目的 ID,以便可以对其执行进一步的操作。我的 areContentsTheSame
实现比较对象中的各个字段以确定项目的内容是否已更改。
问题在于,有时当刷新数据源时,项目的 ID 会发生变化,但可见内容不会发生变化(即相同的项目从数据库中删除,然后再次添加,在此过程中更改其 ID)。在这种情况下,我当前的 areContentsTheSame
实现 returns true
。但是当 areContentsTheSame
returns true
时,RecyclerView
视图不会重新绑定(onBindViewHolder
不会为 areContentsTheSame
的项目调用returns true
),因此视图仍然保留对包含旧 ID 的旧对象的引用,从而在尝试对该项目执行某些操作时导致错误(例如用户点击它以显示它)。
我尝试在 ID 更改时使 areContentsTheSame
return false
强制重新绑定视图,即使没有发生可见的更改,但这会导致 "item changed" 动画显示,即使项目没有明显改变。我想要的是一种在没有 areContentsTheSame
returning false
的情况下强制重新绑定视图的方法,或者是 areContentsTheSame
到 return true
并在不显示动画的情况下触发重新绑定。
实现您想要的效果的最佳方法是在您的 DiffUtil.Callback
实现中覆盖 getChangePayload()
方法。
When areItemsTheSame(int, int)
returns true
for two items and areContentsTheSame(int, int)
returns false
for them, DiffUtil
calls this method to get a payload about the change.
For example, if you are using DiffUtil
with RecyclerView
, you can return the particular field that changed in the item and your ItemAnimator
can use that information to run the correct animation.
Default implementation returns null
.
所以,确保你的 areContentsTheSame()
方法 returns false
当数据库 id 改变时,然后执行 getChangePayload()
来检查这种情况 return 一个非空值。也许是这样的:
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
if (onlyDatabaseIdChanged(oldItemPosition, newItemPosition)) {
return Boolean.FALSE;
} else {
return null;
}
}
从这个方法中 return 什么对象并不重要,只要在您不想看到播放默认更改动画的情况下不为 null 即可。
有关为什么 这行得通的更多详细信息,请参阅此问题的答案:
大部分答案都找出了 DiffUtil 的一些基本示例,其中
代码看起来像
class MyDiffUtil( private val oldList: List<Item>, private val newList:
List<Item> ) : DiffUtil.Callback() {
override fun getOldListSize(): Int = oldList.size
override fun getNewListSize(): Int = newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].id == newList[newItemPosition].id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].id == newList[newItemPosition].id
}
}
当新项目添加或删除具有列表 ID 的项目时,上述代码将正常工作。但是当 id 保持不变但更改一些其他内容时,适配器不会更新,因为代码和平
return oldList[oldItemPosition].id == newList[newItemPosition].id
这里我们检查了 id 而不是其他项目,所以你只需删除 id 并编写这个新代码
class MyDiffUtil( private val oldList: List<Item>, private val newList:
List<Item> ) : DiffUtil.Callback() {
override fun getOldListSize(): Int = oldList.size
override fun getNewListSize(): Int = newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
}
我正在使用 RecyclerView
来显示从数据库中检索为 Java 对象的项目列表。对象中的字段之一是数据库中项目的 ID,以便可以对其执行进一步的操作。我的 areContentsTheSame
实现比较对象中的各个字段以确定项目的内容是否已更改。
问题在于,有时当刷新数据源时,项目的 ID 会发生变化,但可见内容不会发生变化(即相同的项目从数据库中删除,然后再次添加,在此过程中更改其 ID)。在这种情况下,我当前的 areContentsTheSame
实现 returns true
。但是当 areContentsTheSame
returns true
时,RecyclerView
视图不会重新绑定(onBindViewHolder
不会为 areContentsTheSame
的项目调用returns true
),因此视图仍然保留对包含旧 ID 的旧对象的引用,从而在尝试对该项目执行某些操作时导致错误(例如用户点击它以显示它)。
我尝试在 ID 更改时使 areContentsTheSame
return false
强制重新绑定视图,即使没有发生可见的更改,但这会导致 "item changed" 动画显示,即使项目没有明显改变。我想要的是一种在没有 areContentsTheSame
returning false
的情况下强制重新绑定视图的方法,或者是 areContentsTheSame
到 return true
并在不显示动画的情况下触发重新绑定。
实现您想要的效果的最佳方法是在您的 DiffUtil.Callback
实现中覆盖 getChangePayload()
方法。
When
areItemsTheSame(int, int)
returnstrue
for two items andareContentsTheSame(int, int)
returnsfalse
for them,DiffUtil
calls this method to get a payload about the change.For example, if you are using
DiffUtil
withRecyclerView
, you can return the particular field that changed in the item and yourItemAnimator
can use that information to run the correct animation.Default implementation returns
null
.
所以,确保你的 areContentsTheSame()
方法 returns false
当数据库 id 改变时,然后执行 getChangePayload()
来检查这种情况 return 一个非空值。也许是这样的:
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
if (onlyDatabaseIdChanged(oldItemPosition, newItemPosition)) {
return Boolean.FALSE;
} else {
return null;
}
}
从这个方法中 return 什么对象并不重要,只要在您不想看到播放默认更改动画的情况下不为 null 即可。
有关为什么 这行得通的更多详细信息,请参阅此问题的答案:
大部分答案都找出了 DiffUtil 的一些基本示例,其中 代码看起来像
class MyDiffUtil( private val oldList: List<Item>, private val newList:
List<Item> ) : DiffUtil.Callback() {
override fun getOldListSize(): Int = oldList.size
override fun getNewListSize(): Int = newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].id == newList[newItemPosition].id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].id == newList[newItemPosition].id
}
}
当新项目添加或删除具有列表 ID 的项目时,上述代码将正常工作。但是当 id 保持不变但更改一些其他内容时,适配器不会更新,因为代码和平
return oldList[oldItemPosition].id == newList[newItemPosition].id
这里我们检查了 id 而不是其他项目,所以你只需删除 id 并编写这个新代码
class MyDiffUtil( private val oldList: List<Item>, private val newList:
List<Item> ) : DiffUtil.Callback() {
override fun getOldListSize(): Int = oldList.size
override fun getNewListSize(): Int = newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
}