Search code examples
androidfirebasekotlinfirebase-realtime-databaseandroid-recyclerview

Cannot display Data from my layout viewBinding


I am new to Android Studio. I have been following a tutorial on how to create a Kotlin-based MVVM app with Firebase. I created a data class called ServiceOrder that contains all the necessary data fields for my application.

I want to list all the ServiceOrder objects in a RecyclerView. I created a SOListingFragment linked to an adapter (SOListingAdapter). The adapter class manages the data and binds it to the RecyclerView.

When running the app I have been able to add data to the Firebase but the listing layout didn't display any data from the Firestore Database

Here is what I was expecting : Expecteing layout

This is the result at runtime: Result layouton here

Can someone please help me identify where the problem is? I've been stuck on this for weeks now.

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(),)

@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 {
    // Inflate the layout for this fragment
    //return inflater.inflate(R.layout.fragment_s_o_listing, container, false)
   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())

            }
        }
    }


}

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)}
    }

}

}

I implemented a SOListingFragment in an Android application using Kotlin and Hilt for dependency injection. The fragment was designed to display a list of service orders using a RecyclerView with a custom adapter (SOListingAdapter).when running the app I couldn't view the data on the listing menu

My database structure as a JSON file.

{
   "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":{
               
            }
         }
      }
   }
}

Solution

  • The problem in your code lies in the fact that the names of the fields inside the database do not match the ones in the class. I see in the ServiceOrder class a field called SO_numb while in the data is so_numb. See the difference, the SO which are capital letters vs. so which are lowercase letters? To solve this, you either change the names of the fields in the class to match the names in the database or you can use the PropertyName annotation as explained in my answer from the following post: