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,

Другие вопросы по тегам