Home > Enterprise >  Fetching Multiple Firestore Documents for RecyclerView
Fetching Multiple Firestore Documents for RecyclerView

Time:09-02

I'm trying to fetch multiple documents from my Firestore collection so I can populate my RecyclerView. However, I'm getting a mismatch error when I try to hook my categories ArrayList to the QuerySnapshot, it says it's looking for kotlin.collections.ArrayList<Category> but it found Category?. What can I do to make my RecyclerView populate my category collection in Firestore? Do I need to rewrite my val categories = ArrayList<Category>()? Thank you!

Category Collection

enter image description here

Category.kt

data class Category(var category: String?, val categoryImage: String?) : Parcelable {
    constructor(parcel: Parcel) : this(
        parcel.readString(),
        parcel.readString()
    ) {
    }

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(category)
        parcel.writeString(categoryImage)
    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<Category> {
        override fun createFromParcel(parcel: Parcel): Category {
            return Category(parcel)
        }

        override fun newArray(size: Int): Array<Category?> {
            return arrayOfNulls(size)
        }
    }

}

CategoryAdapter.kt

class CategoryAdapter(val category: ArrayList<Category>) : RecyclerView.Adapter<CategoryAdapter.ViewHolder>() {

    var selectedCategory = Category("", "")

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bindCategory(category[position])

        holder.itemView.setOnClickListener { v ->
            val context: Context = v.context
            val intent = Intent(context, CategoryServiceActivity::class.java)
            selectedCategory.category = category[position].category
            intent.putExtra("category", selectedCategory)
            context.startActivity(intent)
        }
    }

    override fun getItemCount(): Int {
        return category.count()
    }

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

    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val categoryName = itemView.findViewById<TextView>(R.id.categoryJobNameTextView)
        val categoryImage = itemView.findViewById<ImageView>(R.id.categoryImageView)

        fun bindCategory(category: Category) {
            categoryName?.text = category.category
            Picasso.get().load(category.categoryImage).into(categoryImage)
        }
    }
}

HomeFragment.kt

val categories = ArrayList<Category>()
val categoriesDatabaseRef = FirebaseFirestore.getInstance().collection(REF_JOB_CATEGORIES)

categoriesDatabaseRef.orderBy("category").get().addOnSuccessListener(object: OnSuccessListener<QuerySnapshot> {
     override fun onSuccess(p0: QuerySnapshot?) {
         if (p0 != null) {
            for (querySnapshot in p0.documents) {
                categories = querySnapshot.toObject(Category::class.java)
            }
         }
     }
})

CodePudding user response:

As I see in your code, the categories object is an ArrayList. So when you're using the following line of code:

categories = querySnapshot.toObject(Category::class.java)

It means that you're trying to convert the querySnapshot object, which is actually a DocumentSnapshot object, into an object of type Category, which works perfectly fine. However, you cannot assign that value to the categories object because between the ArrayList and Category classes, there is no inheritance relationship, hence that error.

So there are two ways in which you can solve this. The first solution would be to add an object of type Category, at each iteration of the for loop to the list:

categoriesDatabaseRef.orderBy("category").get().addOnSuccessListener(object: OnSuccessListener<QuerySnapshot> {
     override fun onSuccess(p0: QuerySnapshot?) {
         if (p0 != null) {
            for (querySnapshot in p0.documents) {
                val category = querySnapshot.toObject(Category::class.java)
                categories.add(category) //Add the object to the list.
            }
         }
     }
})

The second solution, which is even simpler in my opinion, would be to convert the querySnapshot directly into a list, by removing the for loop like this:

categoriesDatabaseRef.orderBy("category").get().addOnSuccessListener(object: OnSuccessListener<QuerySnapshot> {
     override fun onSuccess(p0: QuerySnapshot?) {
         if (p0 != null) {
            categories = p0.toObjects(Category::class.java)
         }
     }
})

Please see that I have used toObjects(Class clazz) method which:

Returns the contents of the documents in the QuerySnapshot, converted to the provided class, as a list.

So it's toObjects, see the s? And not toObject.

Besides that, don't forget that Firebase API is asynchronous. So you cannot simply use the value of categories outside the onSuccess() method. If you're new to asynchronous programming, I recommend you read the following resource:

  • Related