Fragment中notifyDataSetChanged调用后RecyclerView不刷新求助
我正在开发一款应用,将聊天功能迁移至Fragment后功能失效。调试时能看到已经从Firebase获取到消息,适配器的列表不为空,但调用notifyDataSetChanged()后,onBindViewHolder或ViewHolder从未被调用。我尝试在UI线程及回调中执行操作,消息仍无法显示。
聊天Fragment布局文件
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".fragments.saloon.ChatFragment"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerview_chat" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="visible" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </FrameLayout>
Activity布局文件
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorPrimary" tools:context=".activities.SaloonMobileScreenActivity"> <com.google.android.material.tabs.TabLayout android:id="@+id/tabBar" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="parent" app:tabIndicatorColor="@color/colorSecondary" app:tabSelectedTextColor="@color/colorSecondary" app:tabTextAppearance="@style/TabLayoutTextFont" app:tabTextColor="@color/colorPrimaryLight"> <!-- android:id="@+id/tabChat"--> <com.google.android.material.tabs.TabItem android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/chat" /> <!-- android:id="@+id/tabUsers"--> <com.google.android.material.tabs.TabItem android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/users" /> </com.google.android.material.tabs.TabLayout> <androidx.viewpager2.widget.ViewPager2 android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="3dp" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" app:layout_constrainedHeight="true" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tabBar" app:layout_constraintHorizontal_bias="1.0" app:layout_constraintVertical_bias="1.0"> </androidx.viewpager2.widget.ViewPager2> </androidx.constraintlayout.widget.ConstraintLayout>
Activity中的代码
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivitySaloonMobileScreenBinding.inflate(layoutInflater) setContentView(binding!!.root) setTabs() } private fun setTabs() { pagerAdapter = FragmentPagerAdapter(this) pagerAdapter.addFragment(ChatFragment(saloonOwnerUid!!, saloonName!!), resources.getString(R.string.chat)) pagerAdapter.addFragment(UsersFragment(), resources.getString(R.string.users)) binding!!.viewPager.adapter = pagerAdapter binding!!.viewPager.currentItem = 0 TabLayoutMediator(binding!!.tabBar, binding!!.viewPager) { tab, position -> tab.text = pagerAdapter.getTabTitle(position) }.attach() }
Fragment中的代码尝试
private val onDataChangeCb : (ArrayList<MessageModel>) -> Unit = { msgList -> messages!!.clear() messages!!.addAll(msgList) (context as SaloonMobileScreenActivity).runOnUiThread { msgAdapter!!.updateAdapter(msgList) } msgAdapter!!.notifyDataSetChanged() binding!!.recyclerviewChat.smoothScrollToPosition(msgAdapter!!.itemCount) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { // Inflate the layout for this fragment messages = ArrayList() binding = FragmentChatBinding.inflate(layoutInflater) BaseBillboActivity.getDbManager().addChatListener(onDataChangeCb, saloonOwnerUid, saloonName) msgAdapter = MessagesAdapter(requireActivity(), messages!!, senderUid) binding!!.recyclerviewChat.layoutManager = LinearLayoutManager(context) binding!!.recyclerviewChat.adapter = msgAdapter return inflater.inflate(R.layout.fragment_chat, container, false) }
问题分析与解决方案
核心问题
Fragment的onCreateView方法存在致命错误:你先通过FragmentChatBinding.inflate(layoutInflater)初始化了binding对象,并为其中的RecyclerView设置了布局管理器和适配器,但最后返回的却是inflater.inflate(R.layout.fragment_chat, container, false)——这相当于创建了一个全新的、未配置的View实例,之前对binding中RecyclerView的所有设置都没有作用到实际显示的View上,导致RecyclerView根本没有关联到你的适配器。
修复步骤
修正Fragment的视图返回逻辑
将onCreateView的返回值改为binding.root,确保显示的是你配置过的View:override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { messages = ArrayList() binding = FragmentChatBinding.inflate(inflater, container, false) BaseBillboActivity.getDbManager().addChatListener(onDataChangeCb, saloonOwnerUid, saloonName) msgAdapter = MessagesAdapter(requireActivity(), messages!!, senderUid) binding!!.recyclerviewChat.layoutManager = LinearLayoutManager(context) binding!!.recyclerviewChat.adapter = msgAdapter return binding.root }注意:这里使用
FragmentChatBinding.inflate(inflater, container, false)更规范,避免重复创建上下文。统一UI线程操作
回调中notifyDataSetChanged和滚动操作也需要放在UI线程执行,避免线程安全问题:private val onDataChangeCb : (ArrayList<MessageModel>) -> Unit = { msgList -> messages!!.clear() messages!!.addAll(msgList) requireActivity().runOnUiThread { msgAdapter!!.updateAdapter(msgList) msgAdapter!!.notifyDataSetChanged() binding!!.recyclerviewChat.smoothScrollToPosition(msgAdapter!!.itemCount) } }检查适配器的
updateAdapter方法
确保updateAdapter正确更新了适配器内部的数据列表,比如:fun updateAdapter(newList: ArrayList<MessageModel>) { this.messageList.clear() this.messageList.addAll(newList) // 如果这里已经调用了notifyDataSetChanged,外面就不需要重复调用 // notifyDataSetChanged() }如果适配器内部已经处理了通知,外部就不需要再调用
notifyDataSetChanged(),避免重复操作。
内容的提问来源于stack exchange,提问作者Guy Sadoun

