使用 PagingDataAdapter 时如何恢复 recyclerview 滚动位置?
Posted
技术标签:
【中文标题】使用 PagingDataAdapter 时如何恢复 recyclerview 滚动位置?【英文标题】:How to restore recyclerview scroll position when using PagingDataAdapter? 【发布时间】:2020-11-08 00:19:01 【问题描述】:我有一个应用程序,它从 API 中获取 158 个项目的列表,将其存储在 Room 中,并将其显示给用户。 RoomDB 是事实的来源。
这是我的 ViewModel 上从数据库中获取结果的代码:
private val pagingConfig =
PagingConfig(pageSize = 20, enablePlaceholders = false, maxSize = 300)
fun getList(filters: Filters): Flow<PagingData<Character>>
return Pager(pagingConfig)
repository.getCharacters(filters)
.flow.map
it.asDomainModel()
这是填充适配器的片段上的代码:
private fun fetchData(filters: Filters)
lifecycleScope.launch
charactersViewModel.getList(filters).collectLatest pagedList ->
characterAdapter.submitData(pagedList)
当前行为:
当我的第 60 个项目之后发生配置更改时,滚动位置丢失,我发现将 pagingConfig 上的 pageSize 从 20 到 55,它解决了这个问题。我已经尝试过的:
由于我是异步获取数据,因此我尝试使用 article 中的这段代码来防止在适配器为空时加载数据。但它没有工作
characterAdapter.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY
我期望达到的目标:
能够滚动到列表底部并进行配置更改而不会丢失滚动位置“而无需随着列表变大而增加我的 pageSize”
https://github.com/doilio/DC-Characters
【问题讨论】:
【参考方案1】:永远不要从您的 getList
方法返回 Flow<PagingData>
的新实例。
做这样的事情:
class YourViewModel: ViewModel()
private mPagingData = Flow<PagingData<Character>>? = null;
fun getList(filters: Filters): Flow<PagingData<Character>>
if(mPagingData != null) return mPagingData; //This is important
else
mPagingData = Pager(pagingConfig)
repository.getCharacters(filters)
.flow.map
it.asDomainModel()
return mPagingData;
除此之外,请确保在片段的onCreate
中初始化适配器。
【讨论】:
【参考方案2】:您要查找的方法是PagingSource.getRefreshKey()
。它具有先前的状态 PagingState
,其中包含字段 PagingState.anchorPosition
,即上次访问的索引(包括占位符)。
https://developer.android.com/topic/libraries/architecture/paging/v3-network-db#refresh-in-place
编辑:我现在看到您正在使用 Room 的实现。实际上存在一些与 Remote REFRESH 及其 getRefreshKey 实现相关的错误,应该在下一个版本 (alpha07) 中解决。见https://issuetracker.google.com/issues/167260236
【讨论】:
由于作者使用的是 Room,我想知道从 Room 创建的 PagingSource 是否无法按预期工作? (即使在文档中它说“PagingSource 实现为你处理这个”) 啊,我没注意到他们在第一句话中说有空间使用 :) alpha07 修复了一些错误,应该可以解决这个问题。 “alpha07 发布了一些错误修复,应该可以解决这个问题。”听起来不错!!期待它:)【参考方案3】:有同样的问题。在我的情况下,我将 PagingConfig enablePlaceholders 更改为 true,并将 initialLoadSize 设置为可除以 pageSize(例如 pageSize=10,initialLoadSize = 20)。
【讨论】:
【参考方案4】:PagingDataAdapter 有一个名为 loadStateFlow 的 flow 属性,其中包含里面处理过的数据。
你可以这样试试:
lifecycleScope.launchWhenCreated
adapter.loadStateFlow.map it.refresh
.distinctUntilChanged()
.collect
if (it is LoadState.NotLoading)
// reset to position you wanted
LoadState.NotLoading是loadStateFlow的具体状态,对应处理数据完成的状态。
LoadState.NotLoading
【讨论】:
以上是关于使用 PagingDataAdapter 时如何恢复 recyclerview 滚动位置?的主要内容,如果未能解决你的问题,请参考以下文章
Paging 3 - 如何在 PagingDataAdapter 完成刷新并且 DiffUtil 完成差异后滚动到 RecyclerView 的顶部?
Android-利用Jetpack-Compose-+Paging3+swiperefresh实现分页加载,下拉上拉效果