微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Paging 3 - 如何在 PagingDataAdapter 完成刷新和 DiffUtil 完成差异后滚动到 RecyclerView 的顶部?

如何解决Paging 3 - 如何在 PagingDataAdapter 完成刷新和 DiffUtil 完成差异后滚动到 RecyclerView 的顶部?

我将 Paging 3 与 RemoteMediator 一起使用,它在从网络获取新数据时显示缓存数据。 当我刷新我的 PagingDataAdapter(通过调用 refresh())时,我希望我的 RecyclerView 在刷新完成后滚动到顶部。在 https://helm.sh/docs/howto/charts_tips_and_tricks/#automatically-roll-deployments 中,他们尝试通过 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 完成差异时收到通知。但这会导致适配器的 PREPENDAPPEND 加载状态出现各种竞争条件,这也会导致 DiffUtil 运行(但在这里我们不想滚动到顶部)。

一种可行的解决方案是将 PagingData.empty() 传递给 PagingDataAdapter 并重新运行相同的查询(仅调用 refresh 不起作用,因为 PagingData 现在是空,没有任何东西可以刷新)但我更愿意让我的旧数据保持可见,直到我知道刷新确实成功了。

解决方法

查看代码是否刷新了加载类型。

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 为 refresh 清除所有表,则应删除该条件。

if (loadType == LoadType.REFRESH) {
            repoDatabase.remoteKeysDao().clearRemoteKeys()
            repoDatabase.reposDao().clearRepos()
        }
 
,

我已经设法改进主题问题中的基本代码片段。 关键是在 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 Boolean?.isTrue() = this != null && this

所以这里的想法是跟踪 mediator.prepend:endOfPagination 旗国。当中介完成当前页面的分页加载部分时,他的 prepend 状态不会改变(以防您在向下滚动后加载页面)。 解决方案适用于离线和在线模式。

如果您需要在两个方向上跟踪前置分页或分页,那么使用另一个 CombinedLoadStates 属性 append,refresh,mediator 是一个很好的起点和source

,

在搜索静态内容等情况下,我们可以在 falseareItemsTheSame 内返回 DiffUtil.ItemCallback 作为解决方法。我也用它来改变排序属性。

,

@Florian 我可以确认,使用 2021 年 7 月 21 日发布的 postDelayed 版,我们不需要使用 3.1.0-alpha03 滚动到顶部。 此外,我设法进一步过滤了 loadStateFlow 集合,因此它不会阻止 StateRestorationPolicy.PREVENT_WHEN_EMPTY 根据 @Alexandr 答案工作。我的解决方案是:

到我写这篇文章的时候,Paging3 的最新版本是 3.1.0-alpha03 所以导入:

androidx.paging:paging-runtime-ktx:3.1.0-alpha03

然后将您的适配器的恢复策略设置如下:

adapter.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY

如果您对上述更改有编译错误,请确保您使用的 RecyclerView 版本至少为 1.2.0-alpha02。上面的任何版本也不错:

androidx.recyclerview:recyclerview:1.2.0-alpha02

然后使用过滤后的 loadStateFlow 将列表滚动到顶部,仅当您刷新页面并且项目被添加到列表中时:

viewLifecycleOwner.lifecycleScope.launch {
            challengesAdapter.loadStateFlow
                .distinctUntilChanged { old,new ->
                    old.mediator?.prepend?.endOfPaginationReached.isTrue() ==
                            new.mediator?.prepend?.endOfPaginationReached.isTrue() }
                .filter { it.refresh is LoadState.NotLoading && it.prepend.endOfPaginationReached && !it.append.endOfPaginationReached}
                .collect {
                    mBinding.fragmentChallengesByLocationList.scrollToPosition(0)
                }
        }

可以在此处找到 GitHub 讨论:https://github.com/googlecodelabs/android-paging/issues/149

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。