使用MVVM架构跳转其他Fragment后返回时RecyclerView列表重复问题
RecyclerView列表重复问题修复方案
根因
- 全局列表未清空:ViewModel中持有的
arrayListMainUI不会随Fragment销毁而清空,每次加载数据时直接向列表追加新数据,导致旧数据重复累加 - 重复注册Firebase监听器:每次返回Fragment都会执行
loadData(true),重复添加ValueEventListener,同一份数据会触发多次回调,多次插入列表 - ViewModel生命周期绑定问题:使用
requireActivity()初始化ViewModel,ViewModel生命周期和Activity一致,Fragment销毁时ViewModel内的数据、监听器都会保留,返回后重复执行逻辑进一步放大重复问题
修复步骤
1. 修改ViewModel代码
private var listMutableLiveData: MutableLiveData<ArrayList<OffersModelClass>> = MutableLiveData() var arrayListMainUI: ArrayList<OffersModelClass> = ArrayList() val listLiveData: LiveData<ArrayList<OffersModelClass>> get() = listMutableLiveData // 新增:保存监听器引用,避免重复注册 private var valueEventListener: ValueEventListener? = null fun loadData(reload: Boolean) { if (reload && valueEventListener == null) { // 仅未注册监听器时才添加 valueEventListener = object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { // 新增:加载数据前先清空旧列表,避免累加 arrayListMainUI.clear() val tempList = ArrayList<OffersModelClass>() for (data in snapshot.children) { val model = data.getValue(OffersModelClass::class.java) ?: continue val imageID: String = model.imageID.toString() val fileRef11 = FirebaseStorage.getInstance().reference.child("offers/$imageID.jpg") fileRef11.downloadUrl.addOnSuccessListener { uri -> model.ImageUri = uri.toString() model.Time = model.Time?.let { calculateTimeAge(it) } tempList.add(model) // 所有数据加载完成后再更新列表,避免频繁刷新 if (tempList.size == snapshot.childrenCount.toInt()) { arrayListMainUI.addAll(tempList) listMutableLiveData.postValue(arrayListMainUI) } } } } override fun onCancelled(error: DatabaseError) { TODO("Not yet implemented") } } databaseReference.addValueEventListener(valueEventListener!!) } } // 新增:页面销毁时移除监听器,避免内存泄漏 fun removeListener() { valueEventListener?.let { databaseReference.removeEventListener(it) valueEventListener = null } }
2. 修改Fragment代码
recyclerView.layoutManager = LinearLayoutManager(context) recyclerView.setHasFixedSize(true) // 初始化一次Adapter即可,不要每次数据更新都新建 offerAdapter = OfferAdapter(ArrayList()) recyclerView.adapter = offerAdapter viewModel = ViewModelProvider(requireActivity()).get(ViewModel::class.java) if (user != null && viewModel.listLiveData.value == null) { // 仅首次无数据时加载,避免重复请求 viewModel.loadData(true) } viewModel.listLiveData.observe(viewLifecycleOwner, { arrayList -> // 直接更新Adapter数据即可,也可给Adapter自定义updateData方法提升性能 offerAdapter = OfferAdapter(arrayList) recyclerView.adapter = offerAdapter }) // 新增:Fragment销毁时通知ViewModel移除监听器 override fun onDestroyView() { super.onDestroyView() viewModel.removeListener() }
可选优化
如果不需要实时监听Firebase数据变化,直接把addValueEventListener换成addListenerForSingleValueEvent,请求完成后监听器会自动销毁,不需要手动管理,更适合静态列表场景。
内容的提问来源于stack exchange,提问作者Mohammad Mawed
相关产品推荐
相关产品推荐

