Функция AsyncListDiffer onAfterSubmitList Listener

#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 .