Пейджинг 3 - как прокрутить RecyclerView вверх после того, как PagingDataAdapter завершил обновление И DiffUtil завершил различение?

Я использую Paging 3 с RemoteMediator, который показывает кэшированные данные при получении новых данных из сети. Когда я обновляю свой PagingDataAdapter (вызывая на нем refresh()), я хочу, чтобы мой RecyclerView прокручивался вверх после завершения обновления. В codelabs они пытаются справиться с этим с помощью loadStateFlow следующим образом:

lifecycleScope.launch {
    adapter.loadStateFlow
            // Only emit when REFRESH LoadState for RemoteMediator changes.
            .distinctUntilChangedBy { it.refresh }
            // Only react to cases where Remote REFRESH completes i.e., NotLoading.
            .filter { it.refresh is LoadState.NotLoading }
            .collect { binding.list.scrollToPosition(0) }
    }

Это действительно прокручивается вверх, но до завершения работы DiffUtil. Это означает, что если действительно новые данные вставлены вверху, RecyclerView не будет прокручиваться полностью вверх.

Я знаю, что у адаптеров RecyclerView есть обратный вызов AdapterDataObserver, с помощью которого мы можем получить уведомление, когда DiffUtil закончит различать. Но это вызовет всевозможные состояния гонки с PREPEND и APPEND состояниями загрузки адаптера, которые также вызывают запуск DiffUtil (но здесь мы не хотим прокручивать вверх).

Одно из решений, которое могло бы работать, - передать PagingData.empty() в PagingDataAdapter и повторно запустить тот же запрос (простой вызов refresh не сработает, потому что PagingData теперь пуст и обновлять нечего), но я бы предпочел, чтобы мои старые данные были видны до тех пор, пока Я знаю, что обновление действительно прошло.


person Florian Walther    schedule 25.01.2021    source источник
comment
Вы нашли решение?   -  person P1NG2WIN    schedule 05.02.2021
comment
@ P1NG2WIN Нет, сейчас я использую задержку в 300 мс, что отстой   -  person Florian Walther    schedule 05.02.2021
comment
:( Тогда я напишу об этом   -  person P1NG2WIN    schedule 05.02.2021
comment
@ P1NG2WIN вы создали проблему? Если да, можете ли вы связать это здесь?   -  person Florian Walther    schedule 12.02.2021
comment
github.com/googlecodelabs/android-paging/issues/149   -  person P1NG2WIN    schedule 16.02.2021


Ответы (3)


Взгляните на код условия, если loadtype обновляется.

repoDatabase.withTransaction {
            // clear all tables in the database
            if (loadType == LoadType.REFRESH) {
                repoDatabase.remoteKeysDao().clearRemoteKeys()
                repoDatabase.reposDao().clearRepos()
            }
            val prevKey = if (page == GITHUB_STARTING_PAGE_INDEX) null else page - 1
            val nextKey = if (endOfPaginationReached) null else page + 1
            val keys = repos.map {
                Log.e("RemoteKeys", "repoId: ${it.id}  prevKey: $prevKey nextKey: $nextKey")
                RemoteKeys(repoId = it.id, prevKey = prevKey, nextKey = nextKey)
            }
            repoDatabase.remoteKeysDao().insertAll(keys)
            repoDatabase.reposDao().insertAll(repos)
        }

Вы должны удалить условие, если LoadType обновляется, очистите все таблицы.

if (loadType == LoadType.REFRESH) {
            repoDatabase.remoteKeysDao().clearRemoteKeys()
            repoDatabase.reposDao().clearRepos()
        }
 
person jake talledo    schedule 05.05.2021

Мне удалось улучшить фрагмент базового кода из тематического вопроса. Ключ в том, чтобы прослушивать некомбинированные варианты свойств внутри CombinedLoadStates

viewLifecycleOwner.lifecycleScope.launchWhenCreated {
            adapter?.loadStateFlow
                ?.distinctUntilChanged { old, new ->
                    old.mediator?.prepend?.endOfPaginationReached.isTrue() ==
                            new.mediator?.prepend?.endOfPaginationReached.isTrue()
                }
                ?.filter { it.refresh is LoadState.NotLoading }
                ..
                // next flow pipeline operators
        }

где isTrue - логическое расширение fun

fun Boolean?.isTrue() = this != null && this

Итак, идея состоит в том, чтобы отслеживать состояние флага mediator.prepend:endOfPagination. Когда посредник завершил свою часть загрузки подкачки для текущей страницы, его состояние prepend не изменится (в случае, если вы загружаете страницы после прокрутки вниз). Решение хорошо работает как в автономном, так и в онлайн-режимах.

Если вам нужно отслеживать предварительную разбивку по страницам или по страницам в обоих направлениях, это хорошая отправная точка, чтобы поэкспериментировать с другими CombinedLoadStates свойствами _8 _, _ 9 _, _ 10_ и source

person Alexandr Vilkov    schedule 21.05.2021

В случаях, таких как поиск статического содержимого, мы можем вернуть false внутри areItemsTheSame из DiffUtil.ItemCallback в качестве обходного пути. Я использую это также для изменения свойства сортировки.

person Vadim Shved    schedule 18.06.2021