首页 > 解决方案 > 如何正确编写 DiffUtil.Callback 以便 recyclerview 移动动画?

问题描述

我有一个普通旧数据类型的 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 的动作?

目前,它的动画效果就好像该项目被移除并重新插入一样。

标签: androidandroid-recyclerviewandroid-diffutils

解决方案


在我看来,你同时犯了两个错误。

第一个(不确定是否是错误,但通常是相反的方式):您falseareContentsTheSame方法返回。它强制RecyclerView重新绑定视图。如果只是项目的顺序发生了变化,则不是这种方式,只有在false需要重新绑定视图时才返回。所以如果item的内容没有变化,就true默认返回。

第二个嫌疑人是你的setChar方法。似乎DiffCallback一直需要两个相等的列表。如果您在活动/片段中有数据列表并传递相同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总是需要两个不同的列表,一个是旧的,一个是更新的数据。另外,trueareContentsTheSame方法返回


推荐阅读