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
相关产品推荐
相关产品推荐

