2

In my RecyclewView's adapter I am using DiffUtils to update only those items which were modified. While doing this I've noticed that ImageViews are flickering on every update and while debugging I found out that onCreateViewHolder method of my adapter is being called everytime I update the data, so viewholders are not being reused like they should. When I get rid of DiffUtil and use simple notifyDataSetChanged flickering dissapears. So why are those ViewHolders being recreated and how can I fix that?

Here is my DiffUtil callback:

class MyDiffUtilCallback(val newList: List<Item>, val oldList: List<Item>) : DiffUtil.Callback() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
    return newList[newItemPosition].id == oldList[oldItemPosition].id
}

override fun getOldListSize(): Int = oldList.size

override fun getNewListSize(): Int = newList.size

override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
    return newList[newItemPosition] == oldList[oldItemPosition]
}

}

And how I update my adapter:

fun updateItems(items: List<Item>) {
    val diff = DiffUtil.calculateDiff(MyDiffUtilCallback(items, this.items))
    this.items = items
    diff.dispatchUpdatesTo(this)
}

EDIT: I looked into the issue some more and noticed that viewholders are also being recreated when using notifyDataSetChanged, I always thought that during update viewholders are being reused but I guess that is not the case. Anyway image flickering is still visible only when using DiffUtil and not notifyDataSetChanged and I don't know why

matip
  • 794
  • 3
  • 7
  • 27
  • even though your item id is same, newList[newItemPosition] == oldList[oldItemPosition] can return false in every case. Since you are comparing objects. So maybe because of that, you are getting the whole list is changed. – Gautam Apr 11 '20 at 12:52
  • @Gautam `areContentsTheSame` is being called only after `areItemsTheSam` returns true. Also I want `areContentsTheSame` to return false if it's content has change to update it. But that's even beside the point because even when list is changed recycler view shouldn't create new views but reuse old ones – matip Apr 11 '20 at 13:56
  • then i would suggest you to dig deeper. It only means notifydateremoved and notifydatainserted is getting called internally, which is being handled by diffutil. – Gautam Apr 11 '20 at 14:34

1 Answers1

-1

In your code, instead of below code

override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): 
Boolean {
    return newList[newItemPosition] == oldList[oldItemPosition]
}

Use this.

override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): 
Boolean {
    return newList[newItemPosition].id == oldList[oldItemPosition].id
}

because when you compare two objects they compare by memory reference, I think in this case while preparing new list you are not using old lists' same objects, so you should check whether they are equal by using their unique id of your custom data model "Item". But in your case it is calling onCreateViewHolder again, It should not happen here, Try this approach once, and By the way, are you using adapter supporting multiple types of items, If yes, this could be possible, as In changed list you are getting new types of items, which were not created yet, If we did not have those types of items in the previous old list.

akashzincle
  • 1,108
  • 6
  • 15
  • 1
    areContentsTheSame is being called only after areItemsTheSam returns true, and I am using data class so it is not compared by memory reference. Also I want areContentsTheSame to return false if it's content has change to update it. But that's even beside the point because even when list is changed recycler view shouldn't create new views but reuse old ones – matip Apr 11 '20 at 13:58
  • Yes you are right, even If items are changed, new views should not be created, instead previously created views should be recycled, Just one query, For the first time when the old list is there, are the items enough to cover the whole screen of the device, If not, then there might be a possibility, that enough views are not created for the first time, so when second time new list comes, and its size is bigger than old list, then new views will be created – akashzincle Apr 11 '20 at 14:40