Android | Update RecyclerView Adapter on ViewHolder onClick

297 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 answers

0
umar ali Khan 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
Blackbelt 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
Jennifer Karling 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))
            })

    }
}