#android #android-asynclistdiffer
#Android #android-asynclistdiffer
Вопрос:
Я пытаюсь вызвать, recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView,null,0)
только после mDiffer.submitlist(list)
завершения «диффинга» и анимации обновлений / изменений списка.
Существует ли функция AsyncListDiffer для onAfterSubmitList(Callback callback)
, которую я мог бы использовать для достижения этой цели?
Если нет, есть ли способ узнать, когда submitList()
завершается его задача, чтобы я мог вставить туда свой scrollToPosition(0)
?
Ответ №1:
Обновление июль 2020:
Google добавил обратный вызов для этого! Смотрите: <a rel="noreferrer noopener nofollow" href="https://developer.android.com/reference/androidx/recyclerview/widget/AsyncListDiffer#submitList(java.util.List, java.lang.Runnable)» rel=»nofollow noreferrer»>https://developer.android.com/reference/androidx/recyclerview/widget/AsyncListDiffer#submitList(java.util.Список, java.lang.Работоспособный)
Предыдущий ответ, из старых добрых времен:
Прежде всего, я не могу поверить, что Google не предоставил обратный вызов для этого.
Я углубился в исходный код AsyncListDiffer
и обнаружил, что можно получить обратный вызов, когда все обновления RecyclerView будут выполнены, но способ получения этого обратного вызова просто странный.
Вам нужно создать подкласс BatchingListUpdateCallback
, а затем обернуть свой ListUpdateCallback
(или AdapterListUpdateCallback
как в большинстве случаев) внутри него.
Затем вы должны переопределить dispatchLastEvent
. AsyncListDiffer
вызовет это после отправки всех обновлений, кроме одного. В вашем dispatchLastEvent
вы захотите вызвать суперреализацию, чтобы ничего не нарушать. Вы также захотите вызвать пользовательский обратный вызов, который является вашим способом входа.
В Kotlin это выглядит как:
class OnDiffDoneListUpdateCallback(
listUpdateCallback: ListUpdateCallback,
private val onDiffDoneCallback: () -> Unit
) : BatchingListUpdateCallback(listUpdateCallback) {
override fun dispatchLastEvent() {
super.dispatchLastEvent()
onDiffDoneCallback()
}
}
Последний шаг — затем предоставить свой пользовательский OnDiffDoneListUpdateCallback
для AsyncListDiffer
. Для этого вам нужно инициализировать AsyncListDiffer
для себя — если вы используете ListAdapter
или что-то подобное, вам потребуется провести рефакторинг, чтобы вы могли контролировать AsyncListDiffer
.
В Kotlin это выглядит как:
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
класс в свой проект и просто добавьте обратный вызов самостоятельно!
Ответ №2:
Вы можете извлечь выгоду из 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
.