DEV Community

Aniket Kadam
Aniket Kadam

Posted on

Doing Android Long Lists Effectively

You're going to need a RecyclerView no doubt, but making the right choice of adapter can reduce your work tremendously and set you up for success.

If you aren't ever going to change the items in a list while the user's looking at them, this article won't matter to you. But if you are, this will be the simplest, nicest animating, easiest to reason about implementation of it you'd have ever seen.

Normally you'd choose a Recyclerview.ViewHolder but in almost all cases involving changing lists, you're better off choosing the ListAdapter

The key difference between the two, is that the ListAdapter includes an easy way to notify which items have changed by giving us a nearly complete implementation of the DiffUtil.ItemCallback.

Normally, implementing this involves a bit of boilerplate but the ListAdapter abstracts all that out for you.

This handles all the complicated diffing code and you can just focus on providing up-to-date lists of data via sending them to the ListAdapter method

public void submitList(@Nullable List<T> list)

So now all you've got to implement are:

onCreateViewHolder, onBindViewHolder, and the DIFF_CALLBACK.

Then when you have a different list, send it to the adapter.submitList(list).

The first two are standard functions, so their implementation is left as an exercise to the reader.

However, we'll be focusing on the DIFF_CALLBACK. Here's the relevant section:

ListAdapter takes ones of these as a constructor parameter.

Creating a static version of it is great because the interface is built to abstract over comparing two instances at a time, and these are always passed in.

The implementation has two functions, one meant to be a lightweight comparison areItemsTheSame this should be an id or a field that will be compared.
areContentsTheSame does a deep comparison and checks if the objects are exactly the same.
This speeds up comparisons between objects because if they're not the same, a deep comparison doesn't have to be done at all.

I wish the official docs made it a bit clearer!

Here's what that looks like:

class AstronomyPicListAdapter : ListAdapter<AstronomyPic, ApodDetailViewHolder>(DIFF_CALLBACK) {

    companion object { // We create a static version since this doesn't keep state, it only compares the instances it's passed as function parameters.
        val DIFF_CALLBACK = object : DiffUtil.ItemCallback<AstronomyPic>() {
            override fun areItemsTheSame(oldItem: AstronomyPic, newItem: AstronomyPic): Boolean =
                oldItem.date == newItem.date

// Usually a deep comparison.
            override fun areContentsTheSame(oldItem: AstronomyPic, newItem: AstronomyPic): Boolean =
                oldItem == newItem

        }
    }

// Standard oncreateViewHolder using Databinding
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ApodDetailViewHolder {
        return ApodDetailViewHolder(
            DataBindingUtil.inflate<DetailListApodItemBinding>(
                LayoutInflater.from(parent.context),
                R.layout.detail_list_apod_item,
                parent,
                false
            )
        )
    }

// Standard bindViewHolder that uses a bind method and only passes in the data if it was non-null
    override fun onBindViewHolder(holder: ApodDetailViewHolder, position: Int) {
        getItem(position)?.let { holder.bind(it) }
    }


}

What this does for you:

  1. You now only have to get a new list and 'submit' it to the adapter.
  2. The adapter calculates what values have actually changed.
  3. And calls notifyItemChanged on only those specific values.

This results in:

  1. Better animations
  2. Less work for you
  3. Never having to think about notifyItemChanged or dread telling the recyclerview that all times have changed!

Top comments (1)

Collapse
 
vcharantimath profile image
Veeresh Charantimath
  1. areContentsTheSame - is only called if areItemsTheSame returns true

  2. A caveat with ListAdapter - is that it always need to send a new List to perform the diff. Sending the same list will do nothing
    Link - stackoverflow.com/questions/497263...