Reputation: 937
I usually use diffutil
with Recyclerview
when needed. Right now I have situation where the items I get from backend look like this:
data class CarouselItem(var url: String, var pictureUrl: String, var visible: String)
All 3 fields can be the same for 2 items. I am thinking what is the best way to create diffUtil here.
IMHO I should make some wrapper that will add a field by which I can differentiate 2 items (for example ID field).
Also in this case in my diffUtil
, override function areContentsTheSame
would have to compare all 3 fields of item like this:
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].visible == newList[newItemPosition].visible && oldList[oldItemPosition].pictureUrl == newList[newItemPosition].pictureUrl &&
oldList[oldItemPosition].url == newList[newItemPosition].url
}
I think this takes processor power almost the same as when I don't use DiffUtil
since it compares the whole item to the whole item (no benefit of comparing only 1 field).
Is this the right way to do it and is it overkill in this case? I would like to know what are my best option as an android developer when I have situations like this (items like this) in the future? (I like to follow clean code concepts and do things the best way if possible)
Upvotes: 3
Views: 2086
Reputation: 93639
Since you're using a data class, areContentsTheSame()
can be simplified to simply do an equality check:
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
oldList[oldItemPosition] == newList[newItemPosition]
This kind of comparison is typically trivial. The point of checking all the items against each other is to avoid the nuclear option of notifyDataSetChanged()
, which causes a complete do-over of layout of all the views in every row. View layout refreshes are very, very expensive in comparison to property comparisons. (Think about how it's having to compute the size in pixels of every character of text it's going to display in two dimensions and add it all up, etc. whereas comparing two Strings is a very fast comparison of bytes.)
Even if the equality checks were not trivial in comparison to view layout changes, remember that DiffUtil does the equality checks on a background thread, but view layout refreshes must be done on the main thread, so using DiffUtil prevents the UI from feeling janky.
Now if your two lists have nothing in common, DiffUtil will report that and all the views will have to be laid out anyway just like with notifyDataSetChanged()
. DiffUtil might not be a good choice if the entire contents of the list are completely replaced with each change. This might be the case if you're doing some sort of paging with your RecyclerView.
The reason you should use val
instead of var
for items used for DiffUtil is to prevent misuse. var
would work fine as long as you don't use it for items in a list and then expect to be able to compare old and new lists with DiffUtil. areContentsTheSame()
is pointless if you've mutated items in the old list. It might result in the views for a row not getting updated when they should.
As for your question about unique IDs, you can generate them if you need to, but the best way to do that is situation dependent. (How your data is stored, how and when it changes, what is expected to remain constant, etc.) You should have at least one property in your item class that can be used for areItemsTheSame()
's comparison. You can omit that and just compare the two items the same way as you would with areContentsTheSame()
, but you lose the benefits of appropriate animations of items that have had their contents changed while still representing the same thing.
Upvotes: 2