Android | Update RecyclerView Adapter on ViewHolder onClick

312 Views Asked by At

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

            }
        }
3

There are 3 best solutions below

0
On BEST ANSWER

onBindViewHolder you need to save list of fave in change item image base on that list otherwise it changes randomly as view recreates

     fun bind(item: Location) {

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

            }
        }
2
On

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
1
On

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

    }
}