Using ListAdapter with RecyclerView

Android added the ListAdpater in the 27.1.0 Support Library Update which allows for much more performant updating of list items. This library uses the DiffUtil to better calculate changes. Let’s start with some examples.

Getting Setup

First, I recommend starting a new project following the Android guide: https://developer.android.com/guide/topics/ui/layout/recyclerview. Get that set up and we can show the improvement.

Notice, for the previous set up you would have to do the following to change items in your adapter.

fun changeItem(items: String) {
  myDataset = items;
  notifyDataSetChanged()
}

Of you could use the following for inserting to save on performance.

fun addItem(item: String) {
  myDataset.add(item)
  notifyItemInserted(myDataset.size)
}

But what about editing and deleting? That is where the new ListAdpater comes in.

Using the List Adapter

So, first we will create a StringDiffCallback which will extend DiffUtil.ItemCallback. This allows us to creat a custom comparator for our items. In the real world, you would probably have a model that you need to check for difference rather than strings.

class StringDiffCallback : DiffUtil.ItemCallback() {
    override fun areItemsTheSame(oldItem: String?, newItem: String?): Boolean {
        return oldItem == newItem
    }

    override fun areContentsTheSame(oldItem: String?, newItem: String?): Boolean {
        return oldItem == newItem
    }
}

Then, we need to create a new adapter (or update the other one) to extend ListAdapter. We pass this new adapter an instance of our StringDiffCallback to use. Also, we’ve update to allow for the activity to pass a clickListner (for update the items).

class NewAdapter(private val clickListener: (String) -> Unit) :
        ListAdapter(StringDiffCallback()) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        return ViewHolder(inflater.inflate(R.layout.my_text_view, parent, false))
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(getItem(position), clickListener)
    }

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bind(item: String, clickListener: (String) -> Unit) {
            itemView.setOnClickListener { clickListener(item) }
        }
    }
}

Back in our activity we have the following to set up the NewAdapter.

override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_main)

      val myDataset: List = listOf("a", "b", "c")

      viewManager = LinearLayoutManager(this)

      newAdapter = NewAdapter(fun (item: String) {
          // Do some operations on dataset
          newAdapter.submitList(myDataset)
      })
      newAdapter.submitList(myDataset)

      recyclerView = findViewById(R.id.my_recycler_view).apply {
          setHasFixedSize(true)
          layoutManager = viewManager
          adapter = newAdapter
      }
}

The main item to note is the following where we can resubmit our list using the ListAdapter‘s submitList function. That function will use the diff util we passed and update our items accordingly and in a performant way.

newAdapter = NewAdapter(fun (item: String) {
   // Do some operations on dataset
   newAdapter.submitList(myDataset)
}