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

Android Kotlin Fragment中RecyclerView滚动状态保存失败如何解决

问题原因
  • Firebase addValueEventListener 是异步回调,你在 onResume 中执行滚动操作时,列表数据还没加载完成、Adapter还未绑定,滚动操作无效
  • 每次 onDataChange 回调时都会新建Adapter并重新绑定到RecyclerView,会直接重置之前的滚动状态
  • 每次数据回调时直接往 statesList 累加数据,没有清空旧数据,会导致列表数据重复
  • 滚动状态监听时机不合理,仅在滚动状态变化时才保存位置,页面切走时如果没有触发滚动状态变化,会导致保存的位置不是最新值
  • 重复添加Firebase监听,每次Fragment重建onCreateView执行时都会加一次监听,会导致多次回调数据,打乱列表状态
修复方案
  • Adapter初始化提前到onCreateView中,数据加载完成后仅更新列表数据、通知Adapter刷新,不要重复创建和绑定Adapter
  • 数据加载完成、Adapter刷新后再执行滚动位置恢复操作,避免空滚动
  • 优化位置保存逻辑,直接在onPause时获取当前列表的可见位置,不需要额外设置滚动监听
  • 若不需要实时监听Firebase数据变化,改用addListenerForSingleValueEvent仅加载一次数据,避免返回页面时数据更新重置滚动位置
  • onDataChange中加载数据前先清空statesList,避免数据重复
修改后代码
private lateinit var binding: FragmentListBinding
private lateinit var rvAdapter: RvStatesAdapter
private var statesList = ArrayList<State>()
private var databaseReferenceStates: DatabaseReference? = null
private lateinit var sharedPreferences: SharedPreferences
private var lastPosition = 0

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    binding = FragmentListBinding.inflate(inflater, container, false)
    // 提前初始化Adapter,不要每次数据回来都新建
    rvAdapter = RvStatesAdapter(statesList)
    binding.rvStates.apply {
        layoutManager = LinearLayoutManager(requireContext())
        adapter = rvAdapter
    }
    initDatabase()
    getStates()
    return binding.root
}

override fun onResume() {
    super.onResume()
    // 先读取保存的位置,等数据加载完成后再执行滚动
    getLastPositionFromRecyclerView()
}

private fun getLastPositionFromRecyclerView() {
    val statePrefs: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
    lastPosition = statePrefs.getInt("lastPosition", 0)
}

override fun onPause() {
    super.onPause()
    // 页面切走时直接获取当前可见位置,不需要额外设置滚动监听
    lastPosition = (binding.rvStates.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
    saveStateOfRecyclerView()
}

private fun saveStateOfRecyclerView() {
    val statePrefs: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
    val editor: SharedPreferences.Editor = statePrefs.edit()
    editor.putInt("lastPosition", lastPosition)
    editor.apply()
}

private fun getStates() {
    // 不需要实时监听数据就用addListenerForSingleValueEvent,仅加载一次
    databaseReferenceStates?.addListenerForSingleValueEvent(object: ValueEventListener {
        override fun onDataChange(snapshot: DataSnapshot) {
            if (snapshot.exists()) {
                // 先清空旧数据,避免累加出现重复
                statesList.clear()
                for (stateSnapshot in snapshot.children) {
                    val state = stateSnapshot.getValue(State::class.java)
                    state?.let {
                        statesList.add(it)
                    }
                }
                // 仅通知Adapter刷新,不要重新绑定Adapter
                rvAdapter.notifyDataSetChanged()
                // 数据更新完成后再滚动到保存的位置
                binding.rvStates.scrollToPosition(lastPosition)
            }
        }

        override fun onCancelled(error: DatabaseError) {
            // 建议补充异常处理逻辑
        }
    })
}

private fun initDatabase() {
    // Firebase初始化建议放到Application中全局执行,避免重复初始化
    if (FirebaseApp.getApps(requireContext()).isEmpty()) {
        FirebaseApp.initializeApp(requireContext())
    }
    databaseReferenceStates = FirebaseDatabase.getInstance().getReference("States")
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 04:27:01