I'm trying to call recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView,null,0)
only after mDiffer.submitlist(list)
is finished "diffing" and animating the list updates/changes.
Is there an AsyncListDiffer feature for onAfterSubmitList(Callback callback)
that I could use to achieve this?
If not, is there anyway to find out when does submitList()
finish its task so I could put my scrollToPosition(0)
in there?
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 List s are received.
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.
I dived into the source code of AsyncListDiffer, and I found that it's possible to receive a callback when all RecyclerView updates have been done - but the way to receive this callback is just bizarre.
Update July 2020:
Google added a callback for this! See: https://developer.android.com/reference/androidx/recyclerview/widget/AsyncListDiffer#submitList(java.util.List%3CT%3E,%20java.lang.Runnable)
Previous answer, from the bad old days:
First of all, I can't believe Google didn't provide a callback for this.
I dived into the source code of AsyncListDiffer
, and I found that it's possible to receive a callback when all RecyclerView updates have been done - but the way to receive this callback is just bizarre.
You need to create a sub-class of BatchingListUpdateCallback
, and then wrap your ListUpdateCallback
(or AdapterListUpdateCallback
as in most cases) inside it.
Then, you should override dispatchLastEvent
. AsyncListDiffer
will call this after all but one of the updates have been dispatched. In your dispatchLastEvent
, you'll want to call the super implementation, so you don't break anything. You'll also want to invoke a custom callback, which is your way in.
In Kotlin that looks like:
class OnDiffDoneListUpdateCallback(
listUpdateCallback: ListUpdateCallback,
private val onDiffDoneCallback: () -> Unit
) : BatchingListUpdateCallback(listUpdateCallback) {
override fun dispatchLastEvent() {
super.dispatchLastEvent()
onDiffDoneCallback()
}
}
The last step is to then provide your custom OnDiffDoneListUpdateCallback
to the AsyncListDiffer
. To do this, you need to initialise the AsyncListDiffer
for yourself - if you're using ListAdapter
or something similar, you'll need to refactor so that you are in control of the AsyncListDiffer
.
In Kotlin, that looks like:
private val asyncListDiffer = AsyncListDiffer<ItemType>(
OnDiffDoneListUpdateCallback(AdapterListUpdateCallback(adapter)) {
// here's your callback
doTheStuffAfterDiffIsDone()
},
AsyncDifferConfig.Builder<ItemType>(diffCallback).build()
)
Edit: I forgot about the edge-cases, of course!
Sometimes, dispatchLastEvent
isn't called, because AsyncListDiffer
considers the update trivial. Here's the checks it does:
if (newList == mList) {
...
return;
}
// fast simple remove all
if (newList == null) {
...
mUpdateCallback.onRemoved(0, countRemoved);
return;
}
// fast simple first insert
if (mList == null) {
...
mUpdateCallback.onInserted(0, newList.size());
return;
}
I recommend doing these checks for yourself, just before you call asyncListDiffer.submitList(list)
. But of course, it couldn't be that easy! mList
is private, and getCurrentList
will return an empty list if mList
is null, so is useless for this check. Instead you'll have to use reflection to access it:
val listField = AsyncListDiffer::class.java.getDeclaredField("mList")
listField.isAccessible = true
val asyncDifferList = listField.get(asyncListDiffer) as List<ItemType>?
asyncListDiffer.submitList(newList)
if(asyncDifferList == null) {
onDiffDone()
}
if(newList == null) {
onDiffDone()
}
if(newList == asyncDifferList) {
onDiffDone()
}
Edit 2: Now, I know what you're saying - surely there's an easier, less hacky way to do this? And the answer is... yes! Simply copy the entire AsyncListDiffer
class into your project, and just add the callback yourself!
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