如何在Paging Library 3中实现Fragment返回时保留RecyclerView滚动位置
问题根因
滚动位置丢失核心是两个原因:
- 每次Fragment视图重建(返回时)都会重新触发
collectLatest逻辑,重复向已有数据的适配器提交PagingData,导致适配器全局刷新,滚动状态被重置 - RecyclerView状态恢复时机早于PagingData提交完成,导致状态恢复失效
解决方案步骤
1. 移除重复提交逻辑,调整数据流收集时机
不要在onViewCreated中每次重建视图都启动协程收集数据,改为在Fragment首次创建时仅收集一次,避免重复提交PagingData触发刷新:
class MyFragment : Fragment() { // 改用懒加载初始化适配器,全局仅创建一次 private val adapter: FeedAdapter by lazy { FeedAdapter(...) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 仅在Fragment首次创建时收集数据,和Fragment生命周期绑定 lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.CREATED) { viewModel.flow.collectLatest { pagingData -> adapter.submitData(pagingData) } } } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 直接给RecyclerView设置已经持有数据的适配器即可 recyclerView.adapter = adapter // 其余逻辑保留 } }
2. 正确配置适配器状态恢复策略
在给RecyclerView设置适配器之前,就配置状态恢复策略,确保数据加载完成后再恢复滚动位置,之前配置不生效大概率是设置时机晚于setAdapter:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 先设置恢复策略,再绑定适配器 adapter.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY recyclerView.adapter = adapter }
PREVENT_WHEN_EMPTY会在适配器无数据时暂不恢复滚动状态,等PagingData加载完成后自动恢复,完美适配Paging3的异步加载场景。
3. 极端场景手动兜底保存恢复状态
如果以上步骤仍不生效,可以手动保存LayoutManager的滚动状态,在PagingData提交完成后手动恢复:
class MyFragment : Fragment() { private var layoutManagerState: Parcelable? = null override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) // 离开Fragment前保存滚动状态 recyclerView.layoutManager?.let { layoutManagerState = it.onSaveInstanceState() outState.putParcelable("scroll_state", layoutManagerState) } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 恢复之前保存的状态 savedInstanceState?.getParcelable<Parcelable>("scroll_state")?.let { layoutManagerState = it } lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.CREATED) { viewModel.flow.collectLatest { pagingData -> adapter.submitData(pagingData) // 提交数据完成后恢复滚动位置 layoutManagerState?.let { state -> recyclerView.layoutManager?.onRestoreInstanceState(state) layoutManagerState = null // 消费后清空避免重复恢复 } } } } } }
额外优化建议
你的PagingConfig中pageSize设置为5太小,会导致滚动时频繁触发分页加载,恢复位置时对应位置的数据可能还未加载,建议调整到10~30区间,提升滚动流畅度和状态恢复成功率。
内容的提问来源于stack exchange,提问作者Calvin Rai
相关产品推荐
相关产品推荐

