You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RecyclerView.StateRestorationPolicy.PREVENT_WHEN_EMPTY失效,返回时滚动位置不恢复

RecyclerView返回Fragment后无法保留滚动位置的问题解决建议

问题背景

应用中的RecyclerView列表,在用户点击条目进入新Fragment、返回原Fragment时,无法保留之前的滚动位置。尝试过PREVENT_WHEN_EMPTY状态策略、保存布局管理器状态、记录滚动位置等方案均无效,相关代码如下:

Adapter定义

class Adapter(val clickListener: PreviewListener) :
    ListAdapter<DataItem, RecyclerView.ViewHolder>(EntryDiffCallback()) {

Adapter初始化(onViewCreated方法内)

private var previewAdapter: PreviewAdapter? = null

if (previewAdapter == null) {
    previewAdapter =
        PreviewAdapter(
            PreviewListener { info ->
                previewViewModel.updateCurrentInfo(info)
                findNavController()
                    .navigateSafely(
                        PreviewFragmentDirections
                            .actionToExerciseFragment())
            })
    previewAdapter?.stateRestorationPolicy =
        RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY
}
binding.previewListView.adapter = previewAdapter

postponeEnterTransition()

列表数据填充逻辑

previewViewModel.listItems.observe(
    viewLifecycleOwner,
    Observer { list ->
        previewAdapter?.submitList(list)
        (view.parent as? ViewGroup)?.doOnPreDraw { startPostponedEnterTransition() }
    })

附加配置

  • 使用了共享元素动画:
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    sharedElementEnterTransition =
        MaterialContainerTransform().apply {
            duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
            scrimColor = Color.TRANSPARENT
            interpolator = DecelerateInterpolator()
            setAllContainerColors(requireContext().getColorFromAttr(R.attr.colorSurface))
        }
}
  • 在onDestroyView和onDestroy中清理数据,移除后仅触发LeakCanary泄漏提示,无其他改善:
override fun onDestroyView() {
    super.onDestroyView()
    binding.previewListView.adapter = null
    _binding = null
}

override fun onDestroy() {
    super.onDestroy()

    previewAdapter = null
}

解决建议

1. 调整Adapter状态恢复策略

当前PREVENT_WHEN_EMPTY仅在列表为空时阻止状态恢复,返回时列表已有数据的情况下无法生效。尝试改为允许自动恢复:

previewAdapter?.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.ALLOW

2. 移除onDestroyView中清空Adapter的代码

清空Adapter会导致RecyclerView丢失状态引用,改为仅清空Binding,保留Adapter实例(若Fragment为复用状态):

override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
    // 保留adapter引用,避免状态丢失
}

若担心泄漏,可将Adapter与ViewModel绑定,依托ViewModel的生命周期管理实例。

3. 手动控制布局管理器状态的保存与恢复

导航组件默认会保存视图状态,但动态布局管理器可能需要手动处理:

  • 保存状态:在onSaveInstanceState中记录布局状态
override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    binding.previewListView.layoutManager?.let {
        outState.putParcelable("recycler_state", it.onSaveInstanceState())
    }
}
  • 恢复状态:在数据提交完成后恢复,避免因列表为空导致恢复无效
previewViewModel.listItems.observe(viewLifecycleOwner) { list ->
    previewAdapter?.submitList(list) {
        // 数据提交完成后恢复滚动位置
        arguments?.getParcelable<Parcelable>("recycler_state")?.let { state ->
            binding.previewListView.layoutManager?.onRestoreInstanceState(state)
        }
        (view.parent as? ViewGroup)?.doOnPreDraw { startPostponedEnterTransition() }
    }
}

4. 规避共享元素动画的干扰

共享元素动画可能与状态恢复冲突,可尝试延迟恢复滚动位置至动画结束:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    // ... 初始化代码 ...

    postponeEnterTransition()
    view.postDelayed({
        savedInstanceState?.getParcelable<Parcelable>("recycler_state")?.let { state ->
            binding.previewListView.layoutManager?.onRestoreInstanceState(state)
        }
        startPostponedEnterTransition()
    }, sharedElementEnterTransition?.duration ?: 300)
}

5. 检查Fragment导航配置

  • 确认导航图中对应action未设置launchSingleTop="true",该属性可能导致Fragment实例被重建,丢失视图状态。
  • 确保Fragment的retainInstance属性为默认的false,retainInstance=true会导致视图销毁但实例保留,干扰状态保存逻辑。

内容的提问来源于stack exchange,提问作者Ana

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 04:05:54