Android: ListAdapter, a Better Implementation for the RecyclerView

Anubhav
Geek Culture
Published in
3 min readMay 8, 2021
Photo by Nick Fewings on Unsplash

I am assuming you are aware of the RecyclerView and it’s implementation!

Almost every app that we use has some sort of a list, be it a vertical list or a horizontal list. RecyclerView was introduced as a successor to ListView and GridView, and it has been here ever since, rendering any adapter-based views in an efficient way. But there is always some room for improvement, isn’t there?
There comes ListAdapter in the picture. As per the google docs,
ListAdapter is RecyclerView.Adapter base class for presenting List data in a RecyclerView, including computing diffs between Lists on a background thread.

Ok, now what is computing diffs ?
Most of the times only a few parts of our list are getting updated (addition, updation or deletion) and not the whole list data, and when we call notifyDataSetChanged(), it refreshes the whole list. As a result onBindViewHolder() is called for every item of the list; and there comes DiffUtil to our rescue.

DiffUtils is a convenience wrapper around AsyncListDiffer that implements Adapter common default behaviour for item access and counting.

AsyncListDiffer is a helper class for computing the difference between two lists via DiffUtil on a background thread.

Enough theory, let us get to writing some code and understanding the implementation.

The ListAdapter implementation needs a Model class(in our case it is User), a RecylerView.ViewHolder implementation, and a DiffUtil.ItemCallBack<T> as a parameter to the constructor.

RecylerView.ViewHolder and DiffUtil.ItemCallBack<T> snippets

// Model class
data class User(val name: String, val age: Int)

// ViewHolder
class UserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){

fun bind(user: User){
// use the model to bind data to the views
}
}

Above is the basic implementation of a Model class and a ViewHolder class.

private class UserDiffCallBack : DiffUtil.ItemCallback<User>() {
override fun areItemsTheSame(oldItem: User, newItem: User): Boolean =
oldItem == newItem

override fun areContentsTheSame(oldItem: User, newItem: User): Boolean =
oldItem == newItem
}

Here, I have created a class UserDiffCallBack which implements DiffUtil.ItemCallBack<T>, where T is our model class, and in turn we need to override two methods, which compare the items.

Here I have used oldItem == newItem expression for checking if the contents are same or not as we are using data class which provides implementation for equals() method by default.

Now let’s move to the ListAdapter’s implementation.

ListAdapter snippet

class UserRecyclerAdapter :
ListAdapter<User, UserRecyclerAdapter.UserViewHolder>(UserDiffCallBack()) {

class UserViewHolder(itemView: View) :
RecyclerView.ViewHolder(itemView) {

fun onBind(user: User) {
// your implementation
}
}

private class UserDiffCallBack : DiffUtil.ItemCallback<User>() {
override fun areItemsTheSame(oldItem: User, newItem: User): Boolean =
oldItem == newItem

override fun areContentsTheSame(oldItem: User, newItem: User): Boolean =
oldItem == newItem
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
// your implementation
}

override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
// your implementation
}
}

Other than the implementations explained above, we need to override the old OnCreateViewHolder and onBindViewHolder methods, as we did with the RecyclerView implementation.

All that is left to do is initialise the adapter, and provide the list to the adapter.

Initialising the adapter

UserRecyclerAdapter userAdapter = UsersAdapter()//rvUsers is the recyclerview rvUsers.apply {
layoutManager = LinearLayoutManager(
this
)
adapter = userAdapter
}

userViewModel.observe(this) { userList ->
storiesRecyclerAdapter.submitList(userList)
}

ListAdapter comes with a method submitList(List<T> list) to provide new or modified data to the adapter and handles all the diffs computation. As a result we don’t need a setter method to set the list anymore. Diffs will be calculated on background thread and adapter will be notified with the results on the main thread.

That’s the implementation! It removes all the boilerplate code, and makes the development process more efficient and seamless.

--

--