Can I force the RecyclerView Adapter to bind ViewHolders for two more items?

41 Views Asked by At

I'm developing an Android application with RecyclerView. The Layout manager type is GridLayoutManager, the list items are arranged in two columns.

val products = listOf (
    Product("Product 1", "Description of the product 1", 124, 89, 25, 4.5, 1),
    Product("Product 2", "Description of the product 2", 389, 256, 35, 4.2, 12),
    Product("Product 3", "Description of the product 3", 456, 315, 40, 4.3, 36),
    // Total 16 products...
)

val layoutManager = GridLayoutManager(this, 2)
binding.recyclerView.layoutManager = layoutManager

val adapter = ProductAdapter(this)
binding.recyclerView.adapter = adapter
adapter.setData(products)

This is what it looks like: https://i.stack.imgur.com/N9GMA.jpg

The list item itself is a CardView, inside of which there are 11 views (7 TextViews, 2 ImageViews and 2 ImageButtons), organized using ConstraintLayout. You can find the entire layout in my GitHub repository, file item_product.xml.

In the adapter, in the ProductViewHolder class, I set the desired text to the TextViews, the image to the ImageView, and also an OnClickListener for the ImageButton (add to favorites or remove from favorites):

override fun onBindViewHolder(holder: ProductViewHolder, position: Int) {
    val product = currentProducts[position]

    holder.bind(product)
}

inner class ProductViewHolder(view: View) : RecyclerView.ViewHolder(view) {
    private val binding = ItemProductBinding.bind(view)

    private lateinit var product: Product

    fun bind(product: Product) {
        this.product = product

        initFavoriteButton()

        binding.imageView.setImageResource(R.drawable.cream)
        binding.titleTextView.text = product.title
        binding.subtitleTextView.text = product.subtitle
        binding.oldPriceTextView.text = product.oldPrice.toString()
        binding.priceTextView.text = product.price.toString()
        binding.discountTextView.text = context.resources.getString(R.string.discount, product.discount)

        binding.ratingTextView.text = product.rating.toString()
        binding.countTextView.text = context.resources.getString(R.string.feedback_count, product.countReviews)
    }

    private fun initFavoriteButton() {
        setFavoriteButtonIcon()

        binding.addToFavoriteButton.setOnClickListener {
            product.isFavorite = !product.isFavorite
            setFavoriteButtonIcon()
        }
    }

    private fun setFavoriteButtonIcon() {
        if (product.isFavorite) {
            binding.addToFavoriteButton.setImageResource(R.drawable.ic_heart_filled)
        } else {
            binding.addToFavoriteButton.setImageResource(R.drawable.ic_heart_stroke)
        }
    }
}

The problem is that when scrolling the list, and specifically at the moment when the next row of two elements is about to appear on the screen, the list noticeably slows down.

I tried to debug the adapter and discovered this: no more than 6 list items can be displayed on the screen at a time (3 rows of 2 cards each), and when opening the application, the adapter's onBindViewHolder method is called 6 times. This means that the adapter generates view holders only for those elements that are currently visible on the screen. When I'm scrolling through the list and the next two items are about to appear at the bottom of the screen, only then the onBindViewHolder method gets called two more times, at which point the scroll delay occurs.

My question is this: can I somehow force the adapter to call the onBindViewHolder() method not only for those six elements that are visible on the screen, but also for the next row of two elements? As far as I know, this is exactly how RecyclerView should behave by default: prepare for display not only list items currently visible on the screen, but also items that are off the screen. Maybe I need to add some settings for this? Or am I completely mistaken, and the reason for these lags is something else... I would be grateful for any help!

The source code of my project is available here: https://github.com/Vladimir127/GridLayoutManagerExample

0

There are 0 best solutions below