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

Fragment中notifyDataSetChanged调用后RecyclerView不刷新求助

问题:聊天功能迁移至Fragment后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根本没有关联到你的适配器。

修复步骤

  1. 修正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)更规范,避免重复创建上下文。

  2. 统一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)
        }
    }
    
  3. 检查适配器的updateAdapter方法
    确保updateAdapter正确更新了适配器内部的数据列表,比如:

    fun updateAdapter(newList: ArrayList<MessageModel>) {
        this.messageList.clear()
        this.messageList.addAll(newList)
        // 如果这里已经调用了notifyDataSetChanged,外面就不需要重复调用
        // notifyDataSetChanged()
    }
    

    如果适配器内部已经处理了通知,外部就不需要再调用notifyDataSetChanged(),避免重复操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 21:05:57