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

Android MVVM+Firebase应用RecyclerView无法显示Firestore数据求助

RecyclerView无法显示Firestore数据问题排查

我是Android Studio新手,正在学习基于Kotlin的MVVM+Firebase应用开发。已经创建了包含所有所需字段的ServiceOrder数据类,也实现了SOListingFragment和对应的SOListingAdapter,用来通过RecyclerView展示服务订单列表。现在能正常往Firebase里添加数据,但列表完全不显示Firestore中的内容,已经卡了好几周了。

预期效果是展示完整的服务订单列表,实际运行时列表为空。

相关代码

ServiceOrder数据类

data class ServiceOrder(
    val id: String="", 
    val SO_numb: String="", 
    val SO_title: String="", 
    val SO_type: String="", 
    val SO_description: String="", 
    @ServerTimestamp 
    val date: Date= Date(),
)

SOListingFragment

@AndroidEntryPoint class SOListingFragment : Fragment() {
    val TAG: String = "SOListingFragment"
    lateinit var binding: FragmentSOListingBinding
    val viewModel: ServiceOrderViewModel by viewModels()
    val adapter by lazy {
        SOListingAdapter(
            onItemClicked = { pos, item -> } ,
            onEditClicked =  { pos, item -> } ,
            onDeleteClicked = {pos, item ->},
        )
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentSOListingBinding.inflate(layoutInflater)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.recyclerView.adapter = adapter
        binding.button.setOnClickListener{
            findNavController().navigate(R.id.action_SOListingFragment_to_SODetailFragment)
        }
        viewModel.getServiceOrders()
        viewModel.serviceOrder.observe(viewLifecycleOwner){ state ->
            when(state){
                is UiState.Loading -> {
                    binding.progressBar.show()
                }
                is UiState.Failure -> {
                    binding.progressBar.hide()
                    toast(state.error)
                }
                is UiState.Success->{
                    binding.progressBar.hide()
                    adapter.updateList(state.data.toMutableList())
                }
            }
        }
    }
}

SOListingAdapter

class SOListingAdapter ( 
    val  onItemClicked: (Int, ServiceOrder)-> Unit, 
    val  onEditClicked: (Int, ServiceOrder)-> Unit, 
    val onDeleteClicked: (Int, ServiceOrder)-> Unit,
): RecyclerView.Adapter<SOListingAdapter.MyViewHolder>(){
    private var list: MutableList<ServiceOrder> = arrayListOf()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val itemView = ItemSoLayoutBinding.inflate(LayoutInflater.from(parent.context),parent,false)
        return MyViewHolder(itemView)
    }

    fun updateList(list: MutableList<ServiceOrder>){
        this.list = list
        notifyDataSetChanged()
    }

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

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val item = list[position]
        Log.d("SOListingAdapter", "Binding item at position $position: $item")
        holder.bind(item)
    }

    inner class MyViewHolder(val binding: ItemSoLayoutBinding): RecyclerView.ViewHolder(binding.root) { 
        fun bind(item: ServiceOrder){       
            binding.SoNumber.setText(item.SO_numb)
            binding.SoType.setText(item.SO_type)
            binding.SoDescription.setText(item.SO_description)
            binding.SoLayout.setOnClickListener{ onEditClicked.invoke(adapterPosition,item)}
            binding.itemLayout.setOnClickListener{onItemClicked.invoke(adapterPosition,item)}
        }
    }
}

Firestore数据库结构

{
   "collections":{
      "serviceorder":{
         "OInkAxPraLS4WgtqmzEV":{
            "date":{
               "datatype":"timestamp",
               "value":{
                  "_seconds":1719104689,
                  "_nanoseconds":372000000
               }
            },
            "so_description":"ok my man",
            "so_numb":"140678",
            "so_type":"Inspection",
            "so_title":"No Power",
            "id":"OInkAxPraLS4WgtqmzEV",
            "collections":{
               
            }
         },
         "sFxKZZnx5V6RGeoBGwgd":{
            "date":{
               "datatype":"timestamp",
               "value":{
                  "_seconds":1719107360,
                  "_nanoseconds":29000000
               }
            },
            "so_description":"aeet",
            "so_numb":"131493",
            "so_type":"Inspection",
            "so_title":"Maintenance",
            "id":"sFxKZZnx5V6RGeoBGwgd",
            "collections":{
               
            }
         },
         "y6P2kSN7pee86nmVVSF1":{
            "date":{
               "datatype":"timestamp",
               "value":{
                  "_seconds":1719773956,
                  "_nanoseconds":425000000
               }
            },
            "so_description":"free to go man ",
            "so_numb":"143623",
            "so_type":"Fraud Case",
            "id":"y6P2kSN7pee86nmVVSF1",
            "so_title":"Inspection",
            "collections":{
               
            }
         }
      }
   }
}

问题排查与解决

1. 字段命名大小写不匹配

Firestore中的字段是小写开头(so_numb、so_type等),但数据类里是大写开头(SO_numb、SO_type),Firestore序列化时大小写敏感,导致字段无法映射,数据类对象的这些字段会被初始化为空值,列表项绑定后显示为空。

解决方式:

  • 要么修改数据类字段名,和Firestore保持一致:
data class ServiceOrder(
    val id: String="", 
    val so_numb: String="", 
    val so_title: String="", 
    val so_type: String="", 
    val so_description: String="", 
    @ServerTimestamp 
    val date: Date= Date(),
)

同时更新Adapter绑定代码:binding.SoNumber.setText(item.so_numb)

  • 要么用@PropertyName注解指定Firestore字段名:
data class ServiceOrder(
    val id: String="", 
    @PropertyName("so_numb") val SO_numb: String="", 
    @PropertyName("so_title") val SO_title: String="", 
    @PropertyName("so_type") val SO_type: String="", 
    @PropertyName("so_description") val SO_description: String="", 
    @ServerTimestamp 
    val date: Date= Date(),
)

2. RecyclerView未设置LayoutManager

RecyclerView必须设置LayoutManager才能渲染列表项,代码中缺少这一步,即使有数据也无法显示。

解决方式:在onViewCreated中添加:

binding.recyclerView.layoutManager = LinearLayoutManager(requireContext())

3. 检查ViewModel数据读取逻辑与Firestore权限

  • 确认ViewModel的getServiceOrders()方法正确查询serviceorder集合,并转换为ServiceOrder列表:
fun getServiceOrders() {
    serviceOrder.postValue(UiState.Loading)
    FirebaseFirestore.getInstance()
        .collection("serviceorder")
        .get()
        .addOnSuccessListener { snapshot ->
            val orders = snapshot.toObjects(ServiceOrder::class.java)
            serviceOrder.postValue(UiState.Success(orders))
        }
        .addOnFailureListener { e ->
            serviceOrder.postValue(UiState.Failure(e.message ?: "Unknown error"))
        }
}
  • 检查Firestore安全规则,测试阶段可临时开放读写权限(后续需改为严格规则):
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true;
    }
  }
}

4. 添加日志排查

在Fragment的UiState.Success分支添加日志,确认是否获取到数据:

is UiState.Success->{
    binding.progressBar.hide()
    Log.d(TAG, "获取到${state.data.size}条订单数据")
    adapter.updateList(state.data.toMutableList())
}

如果日志显示数据量为0,说明Firestore读取失败;如果数据量大于0但列表没显示,排查Adapter和RecyclerView配置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 12:35:55