如何正确编写 DiffUtil.Callback 以便 recyclerview 确实移动动画?
How to write DiffUtil.Callback correctly so that the recyclerview does move animations?
我有一个普通旧数据类型的 recyclerview 适配器,在我的例子中是 char。
适配器由列表支持。
setChar 方法更新列表。
假设在我的例子中,仅使用与适配器相同的列表调用 setChar,但仅在移动项目时调用。
fun setChar(list: List<Char>) {
val diffResult = DiffUtil.calculateDiff(CharDiffCallBack(mChars, list), true)
mChars = list
diffResult.dispatchUpdatesTo(this)
}
class CharDiffCallBack(private val mOldList: List<Char>, private val mNewList: List<Char>) :
DiffUtil.Callback() {
override fun getOldListSize() = mOldList.size
override fun getNewListSize() = mNewList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = mOldList[oldItemPosition] == mNewList[newItemPosition]
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = false
}
DiffUtil.Callback 的正确实现是什么,以便移动在 recyclerview 移动时正确地动画化?
目前,它的动画效果就像项目被移除并重新插入一样。
我觉得你同时犯了两个错误。
第一个(不确定是不是错了,但通常是相反的方式):你return false
来自areContentsTheSame
方法。它强制 RecyclerView
重新绑定 viewholder。如果仅更改项目的顺序,则不是这种方式,return false
仅当您需要重新绑定 viewholder 时。所以如果项目的内容不改变,默认return true
。
第二个嫌疑人就是你的setChar
方法。似乎 DiffCallback
始终采用两个相等的列表。如果您在 activity/fragment 中有数据列表并传递相同的 List
来设置新数据,那么这就是迫使 DiffCallback
无法正常工作的原因,因为 DiffCallback
不会查看列表中的任何位置变化,因为它们是相等的。
我刚刚测试过,只有这两者的结合才能得到你的结果。那么你应该怎么做:
class YourAdapter(..., charList: List<Char>, ...): RecyclerView.Adapter<...>() {
private val mChars = ArrayList(charList)
// You have to keep a different List object (so, a copy) of the one you pass from the activity
...
fun setChar(list: List<Char>) {
val diffResult = DiffUtil.calculateDiff(CharDiffCallBack(mChars, list), true)
mChars.clear()
mChars.addAll(list)
diffResult.dispatchUpdatesTo(this)
}
}
class CharDiffCallBack(private val mOldList: List<Char>, private val mNewList: List<Char>) :
DiffUtil.Callback() {
...
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = true
}
那样 DiffCallback
总是需要两个 不同的 列表,一个是旧的,一个是更新后的数据。此外,return true
来自 areContentsTheSame
方法
我有一个普通旧数据类型的 recyclerview 适配器,在我的例子中是 char。
适配器由列表支持。
setChar 方法更新列表。
假设在我的例子中,仅使用与适配器相同的列表调用 setChar,但仅在移动项目时调用。
fun setChar(list: List<Char>) {
val diffResult = DiffUtil.calculateDiff(CharDiffCallBack(mChars, list), true)
mChars = list
diffResult.dispatchUpdatesTo(this)
}
class CharDiffCallBack(private val mOldList: List<Char>, private val mNewList: List<Char>) :
DiffUtil.Callback() {
override fun getOldListSize() = mOldList.size
override fun getNewListSize() = mNewList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = mOldList[oldItemPosition] == mNewList[newItemPosition]
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = false
}
DiffUtil.Callback 的正确实现是什么,以便移动在 recyclerview 移动时正确地动画化?
目前,它的动画效果就像项目被移除并重新插入一样。
我觉得你同时犯了两个错误。
第一个(不确定是不是错了,但通常是相反的方式):你return false
来自areContentsTheSame
方法。它强制 RecyclerView
重新绑定 viewholder。如果仅更改项目的顺序,则不是这种方式,return false
仅当您需要重新绑定 viewholder 时。所以如果项目的内容不改变,默认return true
。
第二个嫌疑人就是你的setChar
方法。似乎 DiffCallback
始终采用两个相等的列表。如果您在 activity/fragment 中有数据列表并传递相同的 List
来设置新数据,那么这就是迫使 DiffCallback
无法正常工作的原因,因为 DiffCallback
不会查看列表中的任何位置变化,因为它们是相等的。
我刚刚测试过,只有这两者的结合才能得到你的结果。那么你应该怎么做:
class YourAdapter(..., charList: List<Char>, ...): RecyclerView.Adapter<...>() {
private val mChars = ArrayList(charList)
// You have to keep a different List object (so, a copy) of the one you pass from the activity
...
fun setChar(list: List<Char>) {
val diffResult = DiffUtil.calculateDiff(CharDiffCallBack(mChars, list), true)
mChars.clear()
mChars.addAll(list)
diffResult.dispatchUpdatesTo(this)
}
}
class CharDiffCallBack(private val mOldList: List<Char>, private val mNewList: List<Char>) :
DiffUtil.Callback() {
...
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = true
}
那样 DiffCallback
总是需要两个 不同的 列表,一个是旧的,一个是更新后的数据。此外,return true
来自 areContentsTheSame
方法