This is a question about RecyclerView internal behavior for someone that knows its mechanics or is willing to dig into the source code. I’d like an answer backed up by references to the source.
(scroll down to ‘In other words’ for a more focused question)
I need to understand how notify*
actions (for example, notifyItemInserted()
) are enqueued. Imagine I have an adapter backed up by this list:
ArrayList<String> list = Arrays.asList("one", "three", "four");
I want to add the values zero
and two
, that are missing.
list.add(1, "two"); // notify the view adapter.notifyItemInserted(1); // Seconds later, I go on with zero list.add(0, "zero"); // notify the view adapter.notifyItemInserted(0);
This is pretty straightforward and clear, nothing to tell.
But what if the two actions are very close to each other, and there’s no layout pass in between?
list.add(1, "two"); list.add(0, "zero”);
What should I do now?
adapter.notifyItemInserted(1); adapter.notifyItemInserted(0);
Or maybe
adapter.notifyItemInserted(2); adapter.notifyItemInserted(0);
? From the adapter perspective, the list immediately switched from one, three, four
to zero, one, two, three, four
so the second option seems more reasonable.
list.add(0, “zero”); adapter.notifyItemInserted(0); list.add(2, “two”); adapter.notifyItemInserted(...)
What about it now? 1
or 2
? The list was updated immediately after, but I am sure there was no layout pass in between.
You got the main issue, and I want to know how should I behave in these situations. The real case is that I have multiple asynchronous tasks ending up in an insert()
method. I can enqueue their operations, but:
To update recycler, 4 actions must happen:
adapter.notify*()
getItem*()
and onBind()
on the adapter) and lays out the change.It’s easy to understand this when there’s no concurrency, and they happen in sequence:
1. => 2. => 3. => 4. => (new update) 1. => 2. => 3. => 4. ...
Let’s see what happens between steps.
I want to understand what happens in this case. How should we behave? Should I ensure that step 4 of the previous update did took place before inserting new stuff? If so how?
Step 1 − Create a new project in Android Studio, go to File ⇒ New Project and fill all required details to create a new project. Step 2 − Open build. gradle and add Recycler view & Card view library dependencies. Step 3 − Add the following code to res/layout/activity_main.
To refresh the ListView in Android, call notifyDataSetChanged() method on the Adapter that has been set with the ListView.
I thought about similar questions before, and I decided:
If I want to insert more than 1 item directly to end of list and want to get a animation for all, I should:
list.add("0"); list.add("1"); adapter.notifyItemRangeInserted(5, 2); // Suppose there were 5 items before so "0" has index of 5 and we want to insert 2 items.
If I want to insert more than 1 item directly to end of list, but want to get separated animation for each inserted item, I should:
list.add("0"); list.add("1"); adapter.notifyItemInserted(0); mRecyclerView.postDelayed(new Runnable() { @Override public void run() { // before this happens, Be careful to call other notify* methods. Never call notifyDataSetChanged. adapter.notifyItemInserted(1); } }, mRecyclerView.getItemAnimator().getAddDuration());
Hope this can help.
So lets start from little intro to RecyclerView works with notify items. And works pretty simple with other list of saved ViewGroup items (ListView for ex.)
RecyclerView has Queue of View Items which already drawn. And doesn't know about any your updates, without calling notify(...)
methods. When you added new Items and notify RecyclerView, it starts cycle for checking all Views one by one.
RecyclerView contains and drawn next objects View view-0 (position 0), view-1 (position 1), View-2 (position 2) // Here is changes after updating You added Item View view-new into (position 1) and Notify RecyclerView starts loop to check changes RecyclerView received unmodified view-0(position-0) and left them; RecyclerView found new item view-new(position 1) RecyclerView removing old item view-1(position 1) RecyclerView drawing new item view-new(position 1) // In RecyclerView queue in position-2 was item view-2, // But now we replacing previous item to this position RecyclerView found new item view-1 (new position-2) RecyclerView removing old item view-2(position 2) RecyclerView drawing new item view-1(position 2) // And again same behavior RecyclerView found new item view-3 (new position-3) RecyclerView drawing new item view-1(position 2) // And after all changes new RecyclerView would be RecyclerView contains and drawn next objects View view-0 (position 0), view-new (position 1) view-1 (position 2), View-2 (position 3)
It's just main flow of working notify functions, but what should know all this actions happens on UI Thread, Main Thread, even you can calling updating from Async Tasks. And answering you 2 Question - You can call Notify to the RecyclerView as much as you want, and make sure, you action would be on the correct Queue.
RecyclerView works correct in any usage, more complicated questions would be to your Adapter work. First of all, you need to synchronize you Adapter action, like adding removing items, and totally refuse of index usage. For example, it's would be better for your Example 3
Item firstItem = new Item(0, “zero”); list.add(firstItem); adapter.notifyItemInserted(list.indexOf(firstItem)); //Other action... Item nextItem = new Item(2, “two”); list.add(nextItem); adapter.notifyItemInserted(list.indexOf(nextItem)) //Other actions
UPDATE |
Related to RecyclerView.Adapter Doc, where you can see functions same with notifyDataSetChanged()
. And where this RecyclerView.Adapter
invokes child items with android.database.Observable
extensions, see more About Observable. Access to this Observable Holder is synchronized, until View Element in RecyclerView release usage.
See also RecyclerView from support library version 25.0 Lines 9934 - 9988;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With