override fun getChangePayload(oldItem: Item, newItem: Item): Any? {
//1
if (oldItem.id == newItem.id) {
//2
return if (oldItem.done == newItem.done) {
super.getChangePayload(oldItem, newItem)
} else {
//3
val diff = Bundle()
diff.putBoolean(ARG_DONE, newItem.done)
diff
}
}
return super.getChangePayload(oldItem, newItem)
}
Here’s a step-by-step breakdown of this logic:
The getChangePayload function is called with two arguments: oldItem and newItem. As the name suggests it corresponds to the existing item and the new one. You’re only interested when both objects are the same, in this case, this means they have the same id.
In this example, you’re only considering the field done. So if both objects have the same value you can call its super and return, because there’s nothing more that you can do.
On the contrary, if the values are different you want to identify this change and return it. So you can later access it from the update function.
If you need any more information, please, don’t hesitate to ask :].
If super.getChangePayload(oldItem, newItem) is returned the payloads is going to be empty. So it should trigger the first condition: payloads.isEmpty().
However, the getChangePayload function returns Any?, and we want a Bundle object, it’s a good practice to also check this use case. This is why you’ve got both conditions here.
I noticed that the bind (full binding) method is not guaranteed to be called during scroll thus click listener is outdated when put inside that function of the ViewHolder. I need to move it inside the onBindViewholder itself so that it gets updated while being scrolled. It seems when being scrolled either bind or update will be trigger.
I want to understand the first onBindViewHolder part where we pass emptyList(). Can you further explain as well why we pass emptyList() in the first onBindViewHolder method?
onBindViewHolder is called when the first data is added to the list. Since there are no other data to compare your data, the payload is going to be empty.
override fun onBindViewHolder(viewHolder: ItemViewHolder, pos: Int, payload: List<Any>)
Since it’s necessary to override both onBindViewHolder’s, to avoid duplicating the code, I’m calling the onBindViewHolder with an empty list. All the logic is handled in this second function.
This sample already handles the onClickEvent. If you need to migrate it to the adapter, you need to add its logic when an item is set, otherwise, you might get the listener from a different element in the list.
Hi again, I think we can remove the checking in getChangePayload since it is only called when areItemsTheSame is true and areContentsTheSame is false. The done property in data class Item is in primary constructor and the sample checking in DiffUtil’s areItemsTheSame is utilizing default generated equals. It means (oldItem.done == newItem.done) is always false in getChangePayload unless there is other reason that trigger item’s property changed.
That’s true - good suggestion :]. The reason why I have added those verifications is more of a security for the scenario where another developer changes the object without realizing that it might break something in another place.