How to solve the issue of RecyclerView content messing up after scrolling

Have you been experiencing some irregularities in your recyclerview? Issues like:

  • The recyclerview pre-setting the (wrong) value of some of the views in the list.
  • Some views being visible when they shouldn’t be and vice versa.

and so on…

RecyclerView recycling

99% of the time, this is a problem with recyclerview recycling the views. The 1% percent left could come from human error.

Let’s quickly revisit how RecyclerView works.

How does RecyclerView work?

RecyclerView can easily be called the better ListView. It works just like a ListView — displays a set of data on the screen but uses a different approach for the purpose. RecyclerView, as its name suggests, recycles views once they get out of scope (screen) with the help of ViewHolder pattern.

So how does this affect your app?

Just like I wrote up there, RecyclerView recycles the views once they get out of the screen. While trying to reuse the cache view, binding with the new data sometimes goes wrong. The RecyclerView either retains the old data for the view or fail to reset the view.

Solution

There is an okay solution and there a boom! solution.

1. Reset your views

This may seem trivial but really, a simple thing like resetting your view to their default state during binding could save you a lot of time.

fun bind(question: Question) {
    itemView.rb1.isSelected = false
    itemView.rb2.isSelected = false
    itemView.rb3.isSelected = false
    itemView.rb4.isSelected = false
    
    //Other code goes here
}

What we are basically doing is making sure that we have a default state for the views when our actual data is not being set. This implementation would be different for your different use cases.

2. Use the god-method – Disable recycling

For a situation whereby the first solution above doesn’t work, there is a solution that works like boom!

Add the following line of code in your bindViewHolder() method:

holder.setIsRecyclable(false) 

override fun onBindViewHolder(holder: QuestionsViewHolder, position: Int) {
    holder.setIsRecyclable(false) //Disable recycling

    val question = questions[holder.adapterPosition]
    holder.bind(question)
}

What this line of code does is to basically disable recycling for the recyclerview. That way, new views are created for each list item.

RecyclerView no recycling

Works like charm!

But isn’t this against what RecyclerView is made to do?

You may want to ask the question, isn’t this against what the recyclerview is made to do?

Google deliberately created RecyclerView.ViewHolder.setIsRecyclable() for cases where RecyclerView should not recycle views or recycling views have adverse effects.

NB:

  • A way you can make it more efficient is to take note of which views to recycle and which not to recycle, by handling that based on the number of items in the list.
  • If you are working on an interactive list like mine in the example above, you can find a way to save and keep track of user selection (or any other form of interaction with the views). Like the options chosen by the users as it is in my example above. Reason being that the recyclerview uses entirely new views when scrolling through the list.

Voila!


Thanks for reading. Also, let me know if the solution works for you. And you can give suggestions of other solutions that might have worked for you.

Peace out ✌