0

I have a recyclerview adapter of plain old data type, char in my case.

The adapter is backed by a list.

The setChar method updates the list.

Assume in my case that setChar is only called with the same list as the adapter has but only with items moved.

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
}

What is the correct implementation of DiffUtil.Callback so that the moves are animated correctly as moves by the recyclerview?

Currently, it animates as if the item is removed and reinserted.

Daniel
  • 400
  • 1
  • 2
  • 12

1 Answers1

0

It seems to me that you made two mistakes at the same time.

The first one (not sure if its a mistake, but usually its done the opposite way): you return false from the areContentsTheSame method. It forces RecyclerView to rebind the viewholder. This is not the way if only the order of the item has changed, return false only if you need to rebind the viewholder. So if the contents of items don't change, return true by default.

The second suspect is your setChar method. It seems that DiffCallback takes two equal lists all the time. If you have your data list in activity/fragment and pass the same List to set a new data, then thats the thing forcing DiffCallback to work incorrectly, as DiffCallback doesn't see any position changes in your lists as they are both equal.

I've just tested, and only the combination of those two gives the result you have. So what you should do:

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
}

That way DiffCallback always takes two different lists, one old and one with the updated data. Also, return true from areContentsTheSame method

Teempy
  • 491
  • 4
  • 11