NavigationDrawer切换Fragment后RecyclerView的onCreateViewHolder与onBindViewHolder不调用
问题原因&解决方案
核心bug原因
你的Adapter存在两个数据源引用不同步的问题:
- 初始状态下,你在
FetchFragment中创建的personsList传给Adapter后,Adapter的persons(渲染用数据源)和personsList(过滤用原始数据源)都指向同一个列表对象,所以第一次加载可以正常渲染。 - 只要你触发过一次搜索过滤操作,
publishResults方法会将persons重新赋值为过滤后的新列表,此时persons就和Fragment中的personsList断开了引用关联。 - 后续你在Fragment中更新自己的
personsList、调用notifyDataSetChanged时,Adapter的persons还是指向旧的列表,getItemCount返回的始终是旧列表的长度(大多数情况是0),所以onCreateViewHolder和onBindViewHolder不会触发,RecyclerView自然没有内容。
另外还有两个潜在问题:
- 如果你使用的NavigationDrawer切换逻辑是
hide/show而不是replace,onViewCreated只会在Fragment第一次创建时执行一次,切回Fragment时不会重新拉取数据。 - 你当前用的全局协程没有绑定Fragment生命周期,Fragment销毁后协程还在运行可能导致内存泄漏或空指针异常。
修复步骤
1. 给Adapter添加更新数据的统一方法
在PersonsAdapter中新增如下方法,保证更新数据时两个数据源同步:
fun updateData(newList: List<PersonsDb>) { persons = newList personsList = newList notifyDataSetChanged() }
2. 修改Fragment中数据加载后的更新逻辑
把fetchPPerson方法中原来的adapter.notifyDataSetChanged()替换为调用上面新增的updateData方法,同时优化协程写法绑定生命周期:
// 把原来的CoroutineScope改成viewLifecycleOwner.lifecycleScope private fun fetchPPerson() = viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { try { val querySnapshot = personCollectionRef.get().await() val newList = mutableListOf<PersonsDb>() // 直接创建新列表避免引用问题 for (document in querySnapshot.documents) { val person = document.toObject<Person>() newList.add( PersonsDb( document.id, person?.name.toString(), person?.age.toString().toInt(), Date(person?.dob.toString()) ) ) } withContext(Dispatchers.Main) { adapter.updateData(newList) // 调用统一更新方法 } } catch (e: Exception) { withContext(Dispatchers.Main) { Toast.makeText(requireContext(), e.message, Toast.LENGTH_LONG).show() Log.d("Fetch Error", e.message) } } }
3. 适配Fragment生命周期(可选,针对hide/show切换场景)
如果你的Navigation切换用的是hide/show逻辑,就把fetchPPerson的调用从onViewCreated移到onResume中,保证每次Fragment切到前台都会拉取最新数据:
override fun onResume() { super.onResume() fetchPPerson() }
内容的提问来源于stack exchange,提问作者Kathir
相关产品推荐
相关产品推荐

