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

Kotlin中Firebase实时数据库嵌套数据获取失败求助

问题描述

开发应用时,尝试从Firebase实时数据库获取嵌套数据并在HomeFragment的卡片中展示,但始终无法实现。已通过谷歌、YouTube搜索未找到解决方案,以下是相关代码与数据库结构:


HomeFragment 获取数据函数

private lateinit var mList : MutableList<all_job_details_to_show>

private fun getDataFromFirebase() {

    databaseRef.addValueEventListener(object : ValueEventListener{
        override fun onDataChange(snapshot: DataSnapshot) {
            mList.clear()
            for (taskSnapshot in snapshot.children){
                val job_list= taskSnapshot.key?.let {

                    val jobDetails = taskSnapshot.child("Jobs")
                    all_job_details_to_show(
                        it,
                        all_job_details_to_show.JobDetails(
                            jobDetails.child("startTime").value as String,
                            jobDetails.child("endTime").value as String,
                            taskSnapshot.child("payPerDay").value as Double,
                            taskSnapshot.child("jobTitle").value as String,
                            taskSnapshot.child("description").value as String,
                        ),
                        taskSnapshot.child("businessName").value.toString(),
                        taskSnapshot.child("businessLogo").value.toString(),
                        taskSnapshot.child("address").value.toString(),
                        taskSnapshot.child("ratings").value.toString()?.toDoubleOrNull() ?: 0.0,
                        taskSnapshot.child("jobId").value.toString()
                    )
                }
                if (job_list != null){
                    mList.add(job_list)
                }
            }

            adapter.notifyDataSetChanged()
        }

        override fun onCancelled(error: DatabaseError) {
            Toast.makeText(activity, error.message, Toast.LENGTH_SHORT).show()
        }
    })
}

数据库引用代码

val uid1 = FirebaseAuth.getInstance().uid
getDatabaseRef = FirebaseDatabase.getInstance().reference.child("user_business/$uid1")

数据类定义

data class all_job_details_to_show(
    val businessName : String,
    val businessLogo : String,
    val address :String,
    val ratings: Double,
    val jobId: String

){
    constructor() : this( "","","",0.0,"")

    data class JobDetails(
        val startTime: String,
        val endTime: String,
        val payPerDay: Double,
        val jobTitle: String,
        val description: String,

        ){constructor() : this( "","",0.0,"","")}
}

RecyclerView 适配器代码

class addedJobListAdapter(private val list: MutableList<all_job_details_to_show>):
RecyclerView.Adapter<addedJobListAdapter.addedJobListViewHolder>(){

    private var listener: addedJobListAdapterClickListener? = null

    fun setListener(listener: addedJobListAdapterClickListener){
        this.listener=listener
    }

      inner class addedJobListViewHolder( val binding: JobDescriptionCardBinding): RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): addedJobListViewHolder {

        val binding = JobDescriptionCardBinding.inflate(LayoutInflater.from(parent.context),parent,false)
        return addedJobListViewHolder(binding)
    }

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

    override fun onBindViewHolder(holder: addedJobListViewHolder, position: Int) {

        with(holder){
            with(list[position]){
                binding.address.text= address
                binding.brandName.text = businessName
                binding.ratings.text = ratings.toString()
                binding.jobName.text = JobDetails.jobTitle.toString() // access properties of JobDetails instance
                binding.startTime.text = JobDetails.startTime
                binding.endTime.text = JobDetails.endTime
                binding.rates.text = JobDetails.payPerDay.toString()
                binding.description.text = JobDetails.description.toString()

                Glide.with(itemView.context)
                    .load(businessLogo)
                    .into(binding.brandLogo)

                if (ratings >= 4.50) {
                    binding.ratings.setTextColor(Color.parseColor("#318F2F")) // light green
                } else if (ratings >= 4.00 && ratings < 4.50) {
                    binding.ratings.setTextColor(Color.parseColor("#3CC93A")) // light green
                } else if (ratings >= 3.50 && ratings < 4.00) {
                    binding.ratings.setTextColor(Color.parseColor("#B1AB16"))
                }else if (ratings >= 3.00 && ratings < 3.50) {
                    binding.ratings.setTextColor(Color.parseColor("#E5DE21"))
                }
                else if (ratings >= 2.50 && ratings < 3.00) {
                    binding.ratings.setTextColor(Color.parseColor("#B18616"))
                }else if (ratings >= 2.00 && ratings < 2.50) {
                    binding.ratings.setTextColor(Color.parseColor("#FFC225"))
                }else  {
                    binding.ratings.setTextColor(Color.parseColor("#FF3F25"))
                }

                binding.editButton.setOnClickListener{
                    listener?.onDeleteButtonClicked(this)

                }
                binding.deleteBtn.setOnClickListener {
                    listener?.onEditButtonClicked(this)

                }
            }
        }
    }

    interface addedJobListAdapterClickListener{
        fun  onDeleteButtonClicked(allJobDetailsToShow: all_job_details_to_show)
        fun onEditButtonClicked(allJobDetailsToShow: all_job_details_to_show)
    }
}

Firebase 数据库结构

根节点为user_business,其下包含业务用户的UID节点,每个UID节点内有Jobs子节点(包含startTime、endTime等字段)以及businessName、businessLogo等其他子节点。

数据库结构截图

  • 用户业务根节点与Jobs子节点关系:
    user_business根节点下的Jobs子节点
  • user_business节点下的其他子节点:
    user_business节点下的其他子节点
  • Jobs节点详情:
    Jobs节点的具体字段

解决方案

问题1:数据类定义缺失核心属性

all_job_details_to_show主数据类未定义存储嵌套数据的JobDetails属性,导致数据库数据无法被正确存储,适配器也无法访问嵌套字段。

修复后的数据类:

data class all_job_details_to_show(
    val businessName : String,
    val businessLogo : String,
    val address :String,
    val ratings: Double,
    val jobId: String,
    // 添加JobDetails属性,对应数据库嵌套结构
    val jobDetails: JobDetails
){
    // 无参构造器同步包含jobDetails的默认值
    constructor() : this( "","","",0.0,"", JobDetails())

    data class JobDetails(
        val startTime: String,
        val endTime: String,
        val payPerDay: Double,
        val jobTitle: String,
        val description: String,
    ){
        constructor() : this( "","",0.0,"","")
    }
}

问题2:Firebase数据解析逻辑错误

  1. 数据库引用已定位到单个UID节点,无需遍历snapshot.children;
  2. payPerDay、jobTitle等字段属于Jobs子节点,而非UID节点的直接子节点;
  3. 原初始化all_job_details_to_show时参数顺序错误;
  4. 避免强制类型转换,改用安全转换避免空值崩溃。

修复后的数据获取函数:

private fun getDataFromFirebase() {
    databaseRef.addValueEventListener(object : ValueEventListener{
        override fun onDataChange(snapshot: DataSnapshot) {
            mList.clear()
            val jobDetailsSnapshot = snapshot.child("Jobs")
            val jobItem = all_job_details_to_show(
                snapshot.child("businessName").value.toString(),
                snapshot.child("businessLogo").value.toString(),
                snapshot.child("address").value.toString(),
                snapshot.child("ratings").value.toString()?.toDoubleOrNull() ?: 0.0,
                snapshot.child("jobId").value.toString(),
                all_job_details_to_show.JobDetails(
                    jobDetailsSnapshot.child("startTime").value.toString(),
                    jobDetailsSnapshot.child("endTime").value.toString(),
                    jobDetailsSnapshot.child("payPerDay").value.toString()?.toDoubleOrNull() ?: 0.0,
                    jobDetailsSnapshot.child("jobTitle").value.toString(),
                    jobDetailsSnapshot.child("description").value.toString()
                )
            )
            mList.add(jobItem)
            adapter.notifyDataSetChanged()
        }

        override fun onCancelled(error: DatabaseError) {
            Toast.makeText(activity, error.message, Toast.LENGTH_SHORT).show()
        }
    })
}

问题3:适配器访问嵌套字段方式错误

原代码通过JobDetails.jobTitle调用静态属性,实际应访问当前数据实例的jobDetails成员属性。同时修正了点击事件逻辑颠倒的问题。

修复后适配器的onBindViewHolder方法:

override fun onBindViewHolder(holder: addedJobListViewHolder, position: Int) {
    with(holder){
        with(list[position]){
            binding.address.text= address
            binding.brandName.text = businessName
            binding.ratings.text = ratings.toString()
            // 改为访问当前实例的jobDetails属性
            binding.jobName.text = jobDetails.jobTitle
            binding.startTime.text = jobDetails.startTime
            binding.endTime.text = jobDetails.endTime
            binding.rates.text = jobDetails.payPerDay.toString()
            binding.description.text = jobDetails.description

            Glide.with(itemView.context)
                .load(businessLogo)
                .into(binding.brandLogo)

            // 评级颜色逻辑保持不变...

            binding.editButton.setOnClickListener{
                listener?.onEditButtonClicked(this)
            }
            binding.deleteBtn.setOnClickListener {
                listener?.onDeleteButtonClicked(this)
            }
        }
    }
}

额外注意事项

  1. 确认Firebase实时数据库规则允许读取user_business/$uid1节点数据,否则会触发onCancelled回调;
  2. 所有数据库字段读取都采用toString()或toDoubleOrNull()的安全方式,避免空值导致的崩溃。

内容的提问来源于stack exchange,提问作者Akhil Bhatnagar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 01:52:39