'Android | Update RecyclerView Adapter on ViewHolder onClick

I expect my 'heart' icon to change when my ViewHolder item is clicked. Fortunately, it does this. However, an issue arises as multiple items seems to replicate the button click.

What I mean is: If I tap the heart on item number 1. Other items throughout the list replicate also change the heart. Why is this happening and what is a potential fix? I am confused why this issue is occuring as I am referencing the ViewHolder item. Thus, shouldn't it only affect the item I am clicking?

View Holder

        fun bind(item: Location) {
            heart.setOnClickListener {
                item.fav = item.fav != true
                heart.setImageDrawable(
                when (item.fav) {
                    false -> (ContextCompat.getDrawable(itemView.context, R.drawable.ic_border_heart))
                    else -> (ContextCompat.getDrawable(itemView.context, R.drawable.ic_whole_heart))
                })

            }
        }


Solution 1:[1]

You didn't check the view ID in the onClick method. You can set onClick directly on the views as below.

    class LocationViewHolder(v: View): RecyclerView.ViewHolder(v), View.OnClickListener {

private val actLoc: TextView = v.findViewById(R.id.location_main)
private val genLoc: TextView = v.findViewById(R.id.location_subtitle)
private val heart: ImageView = v.findViewById(R.id.heart)
private lateinit var item: Location

fun bind(item: Location) {
    this.item = item
    actLoc.setText(item.actualLocation)
    actLoc.setOnClickListener {
        Toast.makeText(itemView.context, "${item.cords}", Toast.LENGTH_SHORT).show()
    }
    genLoc.setText(item.genLocation)
    genLoc.setOnClickListener {
        Toast.makeText(itemView.context, "${item.cords}", Toast.LENGTH_SHORT).show()
    }
    heart.setOnClickListener {
        item.fav = item.fav != true
        heart.setImageDrawable(
            when (item.fav) {
                false -> (ContextCompat.getDrawable(itemView.context, R.drawable.ic_border_heart))
                else -> (ContextCompat.getDrawable(itemView.context, R.drawable.ic_whole_heart))
            })

    }
}

Solution 2:[2]

However, an issue arises as multiple items seems to replicate the button click.

it is because of the cell recycling mechanism

heart.setImageDrawable(
   when (item.fav) {
       false -> (ContextCompat.getDrawable(itemView.context, R.drawable.ic_border_heart))
       else -> (ContextCompat.getDrawable(itemView.context, R.drawable.ic_whole_heart))
})

should be part of the bind function in the viewholder and not part of the onClick function. What I would expect is

  • Click informs the viewmodel
  • Viewmodel update the dataset
  • Viewmodel informs the recyclerview

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Jennifer Karling
Solution 2 Blackbelt