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

无法从Firebase实时数据库获取数据到RecyclerView求助

Firebase实时数据库数据加载到RecyclerView失败排查方案

问题核心

日志打印到TEST02后,RestaurantModel对象始终为null,无法正常解析数据库数据。

问题原因分析

  1. 数据库节点读取层级错误
    代码中已将reference指向"RestaurantModel"节点,但遍历子节点时又额外调用userSnapshot.child("RestaurantModel"),而从数据库截图看,每个子节点直接存储餐厅数据,不存在嵌套的RestaurantModel子节点,导致读取路径错误。

  2. 数据类字段与数据库不匹配

    • 数据库中字段名为menu,但数据类中定义为menus,字段名不匹配,Firebase无法映射。
    • 数据库中delivery_charge存储为字符串(如"0"),但数据类中定义为Int类型,Firebase无法自动完成字符串到整数的转换,导致解析失败。
    • 数据类未提供空参构造函数,Firebase反射解析对象时可能出现异常。
  3. 自定义接口命名冲突
    Adapter中自定义的ValueEventListener与Firebase的com.google.firebase.database.ValueEventListener重名,容易导致代码混淆,且实现时可能引发逻辑歧义。

解决方案

1. 修正数据库读取逻辑

修改getRestaurantData方法中的节点读取代码,直接从子节点解析对象:

private fun getRestaurantData() {
    reference = FirebaseDatabase.getInstance().getReference("RestaurantModel")
    reference.addValueEventListener(object : ValueEventListener, RestaurantListAdapter.OnItemClickListener {
        override fun onDataChange(snapshot: DataSnapshot) {
            Log.e("AKSA", "TEST6")
            Log.e("AKSA", "TEST $snapshot")
            restaurantList.clear() // 清空旧数据,避免重复加载
            if (snapshot.exists()) {
                Log.e("AKSA", "TEST00")
                for (userSnapshot in snapshot.children) {
                    Log.e("AKSA", "TEST01")
                    // 直接从子节点解析,不再嵌套读取RestaurantModel
                    val user = userSnapshot.getValue(RestaurantModel::class.java)
                    Log.e("AKSA", "TEST02")
                    if (user != null) {
                        Log.e("AKSA", "TEST03")
                        restaurantList.add(user)
                    }
                }
                val adapter = RestaurantListAdapter(restaurantList, this)
                binding.recyclerViewRestaurant.adapter = adapter
                adapter.notifyDataSetChanged()
            }
        }

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

        override fun onItemClick(restaurantModel: RestaurantModel) {
            val intent = Intent(applicationContext, RestaurantMenuActivity::class.java)
            intent.putExtra("RestaurantModel", restaurantModel)
            startActivity(intent)
            Log.d("AKSA", "MESSAGE")
        }
    })
}

2. 修正数据类字段匹配问题

修改RestaurantModel,确保字段名与数据库一致,调整字段类型并添加空参构造函数:

package com.example.foodtogo.model

import java.io.Serializable

// 给所有参数添加默认值,让Kotlin自动生成空参构造函数
class RestaurantModel(
    val name: String? = null,
    val address: String? = null,
    // 数据库中是字符串类型,先改为String?,后续可转换为Int
    val delivery_charge: String? = null,
    val image: String? = null,
    val open: String? = null,
    // 字段名改为menu,匹配数据库节点
    var menu: List<Menus?>? = null
) : Serializable {}

class Menus(
    val name: String? = null,
    // 数据库中price是字符串,改为String?或确保存数字类型
    val price: String? = null,
    val url: String? = null,
    var totalInCart: Int = 0
) : Serializable {}

如果希望delivery_charge和price保持数值类型,需要修改数据库中对应字段的存储类型为数字(而非字符串),或者添加类型转换逻辑。

3. 重命名自定义接口避免冲突

修改RestaurantListAdapter中的接口名称:

package com.example.foodtogo.view

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.example.foodtogo.R
import com.example.foodtogo.model.RestaurantModel

class RestaurantListAdapter(
    val restaurantList: ArrayList<RestaurantModel>,
    val clickListener: OnItemClickListener
) : RecyclerView.Adapter<RestaurantListAdapter.MyViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val view: View = LayoutInflater.from(parent.context)
            .inflate(R.layout.recycler_restaurantlist, parent, false)
        return MyViewHolder(view)
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.bind(restaurantList[position])
        holder.itemView.setOnClickListener {
            clickListener.onItemClick(restaurantList[position])
        }
    }

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

    inner class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val thumbImage: ImageView = view.findViewById(R.id.thumbImage)
        val restaurantName: TextView = view.findViewById(R.id.restaurantName)
        val restaurantAddress: TextView = view.findViewById(R.id.restaurantAddress)
        val restaurantHours: TextView = view.findViewById(R.id.restaurantHours)

        fun bind(restaurantModel: RestaurantModel?) {
            restaurantName.text = restaurantModel?.name
            restaurantAddress.text = "Address: " + restaurantModel?.address
            restaurantHours.text = "Open: " + restaurantModel?.open
            Glide.with(thumbImage)
                .load(restaurantModel?.image)
                .centerCrop()
                .placeholder(R.drawable.ic_launcher_background)
                .into(thumbImage)
        }
    }

    // 重命名接口,避免与Firebase的ValueEventListener冲突
    interface OnItemClickListener {
        fun onItemClick(restaurantModel: RestaurantModel)
    }
}

额外注意事项

  • 确保Firebase实时数据库的规则允许读取操作(开发阶段可临时设置为read: true)。
  • 检查Glide的依赖是否正确配置,避免图片加载失败。

内容的提问来源于stack exchange,提问作者Aksa Benny

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 03:51:04