While using a ListAdapter I noticed that after updating an item the DiffUtil.ItemCallback areContentsTheSame() method was always returning true. Debugging the code I realized that the old and the new item were exactly the same (the old state disappeared). Therefore the ListAdapter onBindViewHolder() method wasn't being called to update the respective row on the list (only the order of the items changed with a nice animation).
ListAdapter not updating item in reyclerview
ListAdapter not updating when editing content
DiffUtil.Callback not working as expected
Items in Recyclerview + Listadapter won't redraw on update
ListAdapter with DiffUtil.ItemCallback always considers objects the same
When submitting a new list to RecyclerView ListAdapter the diff check always returns true for areContentsTheSame()
However none of the above answers (if any) have provided a correct solution.
The only way it worked for me was by calling notifyDataSetChanged() on the ListAdapter everytime the observer emitted a new result.
But what's the whole point of using a ListAdapter and submitList() to notify changes if all the great performance you could get is thrown away by forcing the RecyclerView to redraw its content (all the items it has showed so far)?
notifyDataSetChanged()
:Even though it works, certainly is not the proper approach to go with if you decided to use a ListAdapter in first place.
viewModel.getObjects().observe(this, listOfObjects -> listAdapter.submitList(new ArrayList<>(listOfObjects)));
:Didn't work.
After updating a item in the list I would like to see the respective changes taking place on the UI (not only the sorting order) for the corresponding row as expected.
I don't know if this is the solution for this specific case, but by the description it sounds exactly like something I went through very recently.
I am working with a new Room + LiveData + ViewModel + DiffUtils integration for the first time and I was having the same problem with updating the items in a RecyclerView after updating the list.
The problem I was having was due to my understanding of updating a list and allowing DiffUtils to do its job as it should. Hopefully the following will be clear enough:
What I was doing:
My mistake was thinking that the problem was in .5 which caused me to spend half a day going back and forth debugging the problem. Eventually I stumbled upon a SO question (cannot find it at the moment) which lead me to the correct source of the problem. That problem is really located in .2 - updating the item in the list.
This is the problem because we are updating our CURRENT adapter list with the new changes even before DiffUtils has a chance of comparing the old and new list changes. What this means is every single time the DiffUtils was always comparing the lists with the old list already containing the changes the new list had.
The solution? Do not update the list item since it is an object and list objects keep the same reference resulting in the same instance being updated everywhere that it is being used/referenced. Instead clone the item object (as in deep clone, I know this might be annoying), apply the changes the user made to that cloned object and then use that clone to update the item entry in the Room Database. DO NOT replace the adapter list item with the clone, leave the original one alone, we only want to update the information in the database, not the list since DiffUtils will take care of that.
What we are essentially doing here is creating an update payload for our Room Database, which will then trigger the LiveData observer into comparing the old list with the new one (containing the updated item data) resulting in the expected change detection between both lists.
Do this:
Don't do this:
A few days ago, I have encountered the same issue. I was trying to update my object inside the adapter like this:
binding.accept.setOnClickListener {
existingEntity.taskStatus = "accept"
listener.onItemClick(existingEntity)
}
The above code was updating the database object but not reflecting on my recyclerview. This happened because of the same reference of an object. So when I was updating the object inside my adapter, it automatically updated my object inside my list. So I changed my code like below:
binding.accept.setOnClickListener {
val newEntity = existingEntity.copy()
newEntity.taskStatus = "accept"
listener.onItemClick(newEntity)
}
So I created a copy object by using Kotlin data class copy() method and it worked for me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With