Differ In RecyclerView

Differ In RecyclerView

 
 
  • RecyclerView is a fundamental component in Android development, widely used to efficiently display large datasets in a scrollable list or grid.
  • As the dataset grows or changes, updating the RecyclerView can become a challenging task.
  • Manually managing these updates may lead to performance issues and visual glitches.
  • Thankfully, Android offers a solution to this problem — the DiffUtils class.
  • In this blog, we will explore the power of DiffUtils in conjunction with RecyclerView and understand how it simplifies and optimizes dataset updates.
 

Understanding DiffUtils:

  • DiffUtils is a utility class provided by Android to compute the difference between two lists and generate a list of updates that can be used to efficiently update a RecyclerView’s adapter.
  • It compares the old and new datasets, identifies the items that have been added, removed, or modified, and creates a list of changes, often referred to as a “Diff result.

There are two way to implement DiffUtilsclass functionality

  1.  
     

    Source Code

    1. Create Activity in your project
    1. Create activity_main.xml file in layout folder
    1. Create a data class
    1. Create card_view_design.xml file
    1. Implement Adapter Class
     
    • Here we extend ListAdapter<T, YourAdapter.ViewHolder> and implement constructor with a class extending DiffUtil.ItemCallback<T> as a parameter which does the data update calculation for us, which is a bit different when compared with the existing RecyclerView Adapter
    • Here we extend ListAdapter and pass TaskDiffCallBack which extends DiffUtilItemCallback<Post>
     
    • This class is a convenience wrapper around AsyncListDiffer that implements Adapter common default behavior for item access and counting.
    DiffUtil.ItemCallback<ItemsViewModel>. callback methods
     
    it is in-build. submitList(List<T>) method
    booksAdapter.submitList(AppUtils.loadData())
     
    • The DiffUtilclass finds the difference between two lists and provides the updated list as an output.
    • This class is used to notify updates to a RecyclerView Adapter. Below is the code that let you know how it differentiate data
     
    1. Implementing AsyncListDiffer
        • For implementing AsyncListDiffer,we use simple Adapter<BooksAdapter.ViewHolder>() adapter
    • In above code we create instance of callback
    private val differCallback = object : DiffUtil.ItemCallback<ItemsViewModel>() { override fun areItemsTheSame(oldItem: ItemsViewModel, newItem: ItemsViewModel): Boolean { return oldItem.id == newItem.id } override fun areContentsTheSame(oldItem: ItemsViewModel, newItem: ItemsViewModel): Boolean { return oldItem == newItem } }
    and register with AsyncListDiffer by making object of it.
    private val differ = AsyncListDiffer(this, differCallback)
     
    • it provides some extra benefits over ListAdapter.
    • It can be connected to a RecyclerView.Adapter, and will signal the adapter of changes between submitted lists.
    • For simplicity, the ListAdapter wrapper class can often be used instead of the AsyncListDiffer directly. This AsyncListDiffer can be used for complex cases, where overriding an adapter base class to support asynchronous List diffing isn't convenient.
    • The AsyncListDiffer can consume the values from a LiveData of List and present the data simply for an adapter. It computes differences in list contents via DiffUtil on a background thread as new Lists are received.
    • Use getCurrentList to access the current List, and present its data objects. Diff results will be dispatched to the ListUpdateCallbackimmmediately before the current list is updated. If you're dispatching list updates directly to an Adapter, this means the Adapter can safely access list items and total size via getCurrentList
     
    1. Create a AppUtils class to generate books data
     

    Why Use DiffUtils with RecyclerView:

    1. Improved Performance: When dealing with a large dataset, manually notifying the RecyclerView of every individual change can be costly and time-consuming. DiffUtils significantly improves performance by efficiently identifying only the necessary changes and updating only the affected items. This optimization leads to smoother scrolling and an enhanced user experience.
    1. No need to use notifyDataSetChanged
    1. Internally it calls DiffUtil.calculateDiff which does the handling for us
    1. it Makes all calculations on background Thread
    1. Seamless Animations: DiffUtils allows RecyclerView to animate the changes smoothly. When items are added, removed, or updated, the RecyclerView gracefully animates the transitions, providing a visually appealing and polished feel to the user interface.
    1. Reduced Memory Consumption: Manually handling dataset updates may lead to memory leaks or unnecessary memory consumption. DiffUtils ensures that only the relevant changes are updated, reducing the memory footprint of the application and promoting better memory management.

    Example :

    NoteAdapter.kt
    package com.example.notemvvmapp.data.adapter import android.app.Activity import android.view.LayoutInflater import android.view.ViewGroup import androidx.navigation.findNavController import androidx.recyclerview.widget.AsyncListDiffer import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import com.example.notemvvmapp.data.model.Note import com.example.notemvvmapp.databinding.NoteLayoutBinding import com.example.notemvvmapp.presentation.fragment.HomeFragmentDirections class NoteAdapter(val activity: Activity) : RecyclerView.Adapter<NoteAdapter.MyViewHolder>() { class MyViewHolder(val binding: NoteLayoutBinding) : RecyclerView.ViewHolder(binding.root) private val differCallback = object : DiffUtil.ItemCallback<Note>() { override fun areItemsTheSame(oldItem: Note, newItem: Note): Boolean { return oldItem.id == newItem.id && oldItem.noteTitle == newItem.noteTitle && oldItem.noteDescription == newItem.noteDescription && oldItem.modifyDate == newItem.modifyDate && oldItem.createDate == newItem.createDate } override fun areContentsTheSame(oldItem: Note, newItem: Note): Boolean { return oldItem == newItem } } val differ = AsyncListDiffer(this, differCallback) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { return MyViewHolder( NoteLayoutBinding.inflate( LayoutInflater.from(parent.context), parent, false ) ) } override fun getItemCount(): Int = differ.currentList.size override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val currentNote = differ.currentList[position] holder.binding.noteTitle.text = currentNote.noteTitle holder.binding.noteDesc.text = currentNote.noteDescription holder.itemView.setOnClickListener { val direction = HomeFragmentDirections.actionHomeFragmentToEditNoteFragment(currentNote) it.findNavController().navigate(direction) } } }
     

    Inside Activity or Fragement Class

    private fun searchNote(query: String?) { val searchQuery = "%$query" noteViewModel.searchNote(searchQuery).observe(this) { notes -> rvAdapter.differ.submitList(notes) } } // method that will return LiveData of List<Note> // observer will notify differ by providing new List // differ will compare and list and change Ui Item accordingly