如何正确编写 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 方法