AsycListDiffer onAfterSubmitList Listener
Я пытаюсь позвонить recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView,null,0)
только после mDiffer.submitlist(list)
закончил "разграничение" и анимацию списка обновлений / изменений.
Есть ли функция AsyncListDiffer для onAfterSubmitList(Callback callback)
что я мог бы использовать для достижения этого?
Если нет, то есть ли вообще узнать, когда submitList()
закончить свою задачу, чтобы я мог поставить свой scrollToPosition(0)
там?
1 ответ
Обновление июль 2020 г.:
Google добавил для этого обратный вызов! См.: https://developer.android.com/reference/androidx/recyclerview/widget/AsyncListDiffer
Предыдущий ответ из старых плохих времен:
Прежде всего, я не могу поверить, что Google не предоставил обратный вызов для этого.
Я погрузился в исходный код AsyncListDiffer
, и я обнаружил, что можно получить обратный вызов, когда все обновления RecyclerView были выполнены, но способ получить этот обратный вызов просто странный.
Вам нужно создать подкласс BatchingListUpdateCallback
, а затем заверните ListUpdateCallback
(или AdapterListUpdateCallback
как и в большинстве случаев) внутри него.
Затем вы должны переопределить dispatchLastEvent
. AsyncListDiffer
вызовет это после отправки всех обновлений, кроме одного. В твоемdispatchLastEvent
, вы захотите вызвать суперреализацию, чтобы ничего не сломать. Вы также захотите вызвать собственный обратный вызов, который вам нужен.
В Котлине это выглядит так:
class OnDiffDoneListUpdateCallback(
listUpdateCallback: ListUpdateCallback,
private val onDiffDoneCallback: () -> Unit
) : BatchingListUpdateCallback(listUpdateCallback) {
override fun dispatchLastEvent() {
super.dispatchLastEvent()
onDiffDoneCallback()
}
}
Последний шаг - предоставить свой собственный OnDiffDoneListUpdateCallback
к AsyncListDiffer
. Для этого вам нужно инициализироватьAsyncListDiffer
для себя - если вы используете ListAdapter
или что-то подобное, вам нужно будет провести рефакторинг, чтобы контролировать AsyncListDiffer
.
В Котлине это выглядит так:
private val asyncListDiffer = AsyncListDiffer<ItemType>(
OnDiffDoneListUpdateCallback(AdapterListUpdateCallback(adapter)) {
// here's your callback
doTheStuffAfterDiffIsDone()
},
AsyncDifferConfig.Builder<ItemType>(diffCallback).build()
)
Изменить: я, конечно, забыл о крайних случаях!
Иногда, dispatchLastEvent
не называется, потому что AsyncListDiffer
считает обновление тривиальным. Вот проверки, которые он выполняет:
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;
}
Я рекомендую сделать эти проверки самостоятельно, незадолго до звонка asyncListDiffer.submitList(list)
. Но, конечно, это не могло быть так просто!mList
является частным, и getCurrentList
вернет пустой список, если mList
имеет значение null, поэтому бесполезен для этой проверки. Вместо этого вам придется использовать отражение для доступа к нему:
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()
}
Изменить 2: Теперь я знаю, о чем вы говорите - конечно, есть более простой и менее хакерский способ сделать это? И ответ... да! Просто скопируйте весьAsyncListDiffer
class в свой проект и просто добавьте обратный вызов самостоятельно!
Вы можете получить преимущество от registerAdapterDataObserver
методы, слушая их (или то, что вам нужно) то есть:
listAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
//
}
override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
}
override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) {
}
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
}
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
}
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
}
})
Вы можете очистить реестр вашего adapterObserver
когда-то нужно с помощью unregisterAdapterDataObserver
,