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

Kotlin中Firebase实时数据库数据无法在RecyclerView显示求助

Firebase实时数据库数据无法显示在RecyclerView

尝试将Firebase实时数据库groupNotice/(userId)路径下的数据展示到RecyclerView中,但数据始终无法加载显示。程序运行无报错,已尝试修改数据库引用路径、调整RecyclerView宽高设置,仍未解决。


相关代码

fragment_group_notice.xml

<?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:id="@+id/GroupNoticeFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".groupTab.groupNotices.GroupNoticesFragment">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/groupNoticeListRecycler"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager">

        </androidx.recyclerview.widget.RecyclerView>

</FrameLayout>

card_group_notice.xml

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android">
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:background="@color/primary_green"
        android:orientation="horizontal"
        android:padding="10dp">
        
        <TextView
            android:id="@+id/NoticeNameCard"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Notice Name"
            android:textColor="@color/pure_white"
            android:textSize="25sp" />

    </LinearLayout>

</layout>

GroupNoticesFragment.kt

class GroupNoticesFragment : Fragment() {
    private var _binding: FragmentGroupNoticesBinding? = null
    private val binding get() = _binding!!
    private lateinit var recycler: RecyclerView
    private  var adapter: NoticeAdapter?= null
    private lateinit var viewModel: NoticeViewModel

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        container?.removeAllViews()
        _binding = FragmentGroupNoticesBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        recycler = binding.groupNoticeListRecycler
        recycler.layoutManager = LinearLayoutManager(context)
        recycler.setHasFixedSize(true)
        adapter = NoticeAdapter()
        recycler.adapter = adapter
        viewModel = ViewModelProvider(this)[NoticeViewModel::class.java]

        viewModel.allNotices.observe(viewLifecycleOwner) {
            adapter!!.updateNoticeList(it)
        }

    }
}

NoticeAdapter.kt

class NoticeAdapter : RecyclerView.Adapter<NoticeAdapter.NoticeViewHolder>() {
    private val notices = ArrayList<GroupNoticeData>()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoticeViewHolder {
        val itemView = LayoutInflater.from(parent.context).inflate(
            R.layout.card_group_notices,
            parent, false
        )
        return NoticeViewHolder(itemView)
    }

    override fun onBindViewHolder(holder: NoticeViewHolder, position: Int) {
        val currentNotice = notices[position]
        holder.noticeName.text = currentNotice.taskName
    }

    override fun getItemCount(): Int {
        return notices.size
    }

    fun updateNoticeList(notice: List<GroupNoticeData>) {
        this.notices.clear()
        this.notices.addAll(notice)
        notifyDataSetChanged()
    }

    class NoticeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val noticeName: TextView = itemView.findViewById(R.id.NoticeNameCard)
    }
}

GroupNoticeData.kt

data class GroupNoticeData(
    val userId: String,
    val taskName: String,
    val taskDescription: String,
    val taskDate: String,
    val path: String?,
    val groupId: String
) {
    fun toMap(): Map<String, Any?> {
        return mapOf(
            "userId" to userId,
            "taskName" to taskName,
            "taskDescription" to taskDescription,
            "taskDate" to taskDate,
            "path" to path,
            "groupId" to groupId
        )
    }
}

NoticeViewModel.kt

class NoticeViewModel : ViewModel() {
    private val repository : NoticeRepo = NoticeRepo().getInstance()
    private val _allNotices = MutableLiveData<List<GroupNoticeData>>()
    val allNotices : LiveData<List<GroupNoticeData>> = _allNotices

    init {
        repository.loadNotices(_allNotices)
    }
}

NoticeRepo.kt

class NoticeRepo{
    val auth = Firebase.auth
    val user = auth.currentUser!!.uid
    private val noticeReference : DatabaseReference = FirebaseDatabase.getInstance().getReference("groupNotice").child(user)
    @Volatile private var INSTANCE : NoticeRepo?= null

    fun getInstance(): NoticeRepo {
        return INSTANCE ?: synchronized(this) {
            val instance = NoticeRepo()
            INSTANCE = instance
            instance
        }
    }

    fun loadNotices(allNoticeList: MutableLiveData<List<GroupNoticeData>>) {
        noticeReference.addValueEventListener(object : ValueEventListener {
            override fun onDataChange(snapshot: DataSnapshot) {
                try {
                    val noticeList : List<GroupNoticeData> = snapshot.children.map { dataSnapshot ->
                        dataSnapshot.getValue(GroupNoticeData::class.java)!!
                    }
                    allNoticeList.postValue(noticeList)
                }catch (_: Exception){
                }
            }

            override fun onCancelled(error: DatabaseError) {
                TODO("Not yet implemented")
            }
        })
    }
}

问题排查与修复方案

1. 数据类添加无参构造函数

Firebase反序列化Kotlin数据类必须依赖无参构造,否则无法将Snapshot转换为实体类。修改GroupNoticeData.kt:

data class GroupNoticeData(
    val userId: String = "",
    val taskName: String = "",
    val taskDescription: String = "",
    val taskDate: String = "",
    val path: String? = null,
    val groupId: String = ""
) {
    fun toMap(): Map<String, Any?> {
        return mapOf(
            "userId" to userId,
            "taskName" to taskName,
            "taskDescription" to taskDescription,
            "taskDate" to taskDate,
            "path" to path,
            "groupId" to groupId
        )
    }
}

2. 修正单例模式实现

原NoticeRepo的单例逻辑错误,导致数据库引用重复初始化,且未处理用户未登录场景。修改NoticeRepo.kt:

class NoticeRepo private constructor() {
    private lateinit var noticeReference: DatabaseReference
    private val auth = Firebase.auth

    init {
        auth.currentUser?.let { user ->
            noticeReference = FirebaseDatabase.getInstance().getReference("groupNotice").child(user.uid)
        } ?: run {
            Log.e("NoticeRepo", "用户未登录,无法初始化数据库引用")
        }
    }

    companion object {
        @Volatile
        private var INSTANCE: NoticeRepo? = null

        fun getInstance(): NoticeRepo {
            return INSTANCE ?: synchronized(this) {
                val instance = NoticeRepo()
                INSTANCE = instance
                instance
            }
        }
    }

    fun loadNotices(allNoticeList: MutableLiveData<List<GroupNoticeData>>) {
        if (!::noticeReference.isInitialized) return
        
        noticeReference.addValueEventListener(object : ValueEventListener {
            override fun onDataChange(snapshot: DataSnapshot) {
                try {
                    val noticeList = snapshot.children.mapNotNull {
                        it.getValue(GroupNoticeData::class.java)
                    }
                    allNoticeList.postValue(noticeList)
                    Log.d("NoticeRepo", "成功加载${noticeList.size}条通知")
                } catch (e: Exception) {
                    Log.e("NoticeRepo", "解析通知失败", e)
                }
            }

            override fun onCancelled(error: DatabaseError) {
                Log.e("NoticeRepo", "数据库加载取消", error.toException())
            }
        })
    }
}

3. 修正Adapter布局引用

NoticeAdapter中加载的布局ID与实际布局文件名不匹配,修改为正确的ID:

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoticeViewHolder {
    val itemView = LayoutInflater.from(parent.context).inflate(
        R.layout.card_group_notice, // 修正布局ID
        parent, false
    )
    return NoticeViewHolder(itemView)
}

4. 调整RecyclerView高度

将RecyclerView高度改为match_parent,避免wrap_content导致数据加载后无法显示:

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/groupNoticeListRecycler"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager">
</androidx.recyclerview.widget.RecyclerView>

内容的提问来源于stack exchange,提问作者Parvez Ahammed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 19:10:32