63

So I have an activity with RecyclerView and I want to change TextView of every item in the RecyclerView by pressing button that has onClickListener() in the activity.

I'm wondering what is better in terms of performance:

  1. Use notifyDataSetChanged ones.
  2. Use loop with condition like int i is less than List.size() where notifyItemChanged would be called few times.

In both cases I create boolean variable in RecyclerView Adapter which is used by onBindViewHolder to know how to update item. By default it's false and after button click it becomes true so onBindViewHolder updates item in different way.

Also I would like to know if this approach is suitable at all.

Mauker
  • 11,237
  • 7
  • 58
  • 76
Daniel
  • 993
  • 2
  • 10
  • 23
  • `notifyDataSetChanged` will redraw the visible views. If I am understanding your requirements correctly, I would choose that. – Dan Harms Nov 18 '15 at 20:17
  • Have you looked into the RecyclerView code yet to see what's happening in `notifyDataSetChanged`? Based solely on the name (and not expertise) it sounds like what you want because you're revalidating the entire data set at once, instead of calling it on individual items which seems tedious unless you're changing a specific item. – AdamMc331 Nov 18 '15 at 20:17
  • @McAdam331 my RecyclerView's item is CardView with TextView in it. I want to change the TextView in all visible cards at once. – Daniel Nov 18 '15 at 20:32

3 Answers3

83

If you are simply updating one part of the view, use the notifyItemRangeChanged()or notifyItemChanged() instead of notifiyDataSetChanged(). The difference here has to do with structural changes vs item changes. This is on the android developers RecyclerView.Adapter documentation found here.

Here is another tidbit on the differences between the two types of changes:

There are two different classes of data change events, item changes and structural changes. Item changes are when a single item has its data updated but no positional changes have occurred. Structural changes are when items are inserted, removed or moved within the data set.

This is taken from that aforementioned page,

If you are writing an adapter it will always be more efficient to use the more specific change events if you can. Rely on notifyDataSetChanged() as a last resort.

So just to clarify use notifyDataSetChanged() as a last resort, and instead ask yourself if you can preform one of these methods instead, and if you can use it instead:

notifyItemChanged(int)
notifyItemInserted(int)
notifyItemRemoved(int)
notifyItemRangeChanged(int, int)
notifyItemRangeInserted(int, int)
notifyItemRangeRemoved(int, int)

which makes sense because notifyDataSetChanged() will pretty much try to redraw everything based on the data and make no previous assumptions on it, while the other methods will just look for changes. That means the adapter has to do a lot more work that is not necessary. This is what notifyDataSetChanged() does:

This event does not specify what about the data set has changed, forcing any observers to assume that all existing items and structure may no longer be valid. LayoutManagers will be forced to fully rebind and relayout all visible views.

This also makes sense to use the incremental or range approach, because you are changing the text, you need to go get each new text and when you do that you should tell the adapter you changed it. Now, if you do a button click and get all new text values, and create a new list or something then call heavy notifyDataSetChanged().

Evin1_
  • 12,292
  • 9
  • 45
  • 47
napkinsterror
  • 1,915
  • 4
  • 18
  • 27
  • What I experienced a days ago is that calling notifyItenChanged(x) not only calls onBindViewHolder for x, but also for the other items where the hash of the viewholder changed, I still don't know if this is an Android bug though, or it's a common behavior. – David Feb 17 '18 at 12:34
16

I would definitely call notifyDataSetChanged() if all of the data items ar no longer valid. When you call notifyItemChanged(mPos), it is equivalent to a call to notifyItemRangeChanged(mPos, 1), and every time it is called, requestLayout() is also called. On the other hand, when you call notifyDataSetChanged() or notifyItemRangeChanged(0, mList.size()), there is only one call to requestLayout().

Your question should now be, what is better, a call to notifyDataSetChanged() or notifyItemRangeChanged(0, mList.size())? For that one I don't have an answer.

Ari
  • 3,086
  • 1
  • 19
  • 27
  • 3
    My adapter has stable IDs and using `notifyItemRangeChanged(0, list.size())` proved extremely slow (as much as 3 sec for a 100 items data set) compared to `notifyDataSetChanged()` that was nearly instant. With the former, the adapter was recreating a lot of view holders for some reason whereas with the latter they were all recycled. – Nicolas Sep 09 '18 at 18:49
10

I've noticed that notifyItemChanged(mPos) triggers onBindVieHolder for corresponding position even it's currently not visible.

For me, calling it in a loop for all elements was more costly than notifyDatasetChanged which redrawn only visible ones.

So be careful with large datasets.

Zbigniew Malinowski
  • 1,034
  • 1
  • 9
  • 22