使用 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 上的 pageSize20 55,它解决了这个问题。

我已经尝试过的:

由于我是异步获取数据,因此我尝试使用 article 中的这段代码来防止在适配器为空时加载数据。但它没有工作

characterAdapter.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY

我期望达到的目标:

能够滚动到列表底部并进行配置更改而不会丢失滚动位置“而无需随着列表变大而增加我的 pageSize

https://github.com/doilio/DC-Characters

【问题讨论】:

【参考方案1】:

永远不要从您的 getList 方法返回 Flow&lt;PagingData&gt; 的新实例。

做这样的事情:

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 有一个名为 loadStateFlowflow 属性,其中包含里面处理过的数据。

你可以这样试试:

lifecycleScope.launchWhenCreated 
        adapter.loadStateFlow.map  it.refresh 
            .distinctUntilChanged()
            .collect 
                if (it is LoadState.NotLoading) 
                    // reset to position you wanted
                
            
    

LoadState.NotLoadingloadStateFlow的具体状态,对应处理数据完成的状态。

LoadState.NotLoading

【讨论】:

以上是关于使用 PagingDataAdapter 时如何恢复 recyclerview 滚动位置?的主要内容,如果未能解决你的问题,请参考以下文章

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

#yyds干货盘点#RAID阵列恢 复 测 试

李炎恢老师模型初步(下)5

重新加载时保存复选框状态

Android-利用Jetpack-Compose-+Paging3+swiperefresh实现分页加载,下拉上拉效果

如何将两个请求主体设置为一个 RecyclerView?