Search code examples
kotlinandroid-recyclerviewadapteronclicklistenerandroid-viewholder

Set onclick in ViewHolder for classrecyclerView item collapse in kotlin


I'm trying to set a simple collapsing item in my recyclerView. I saw many applications in Java, but only a few in Kotlin. Almost the onClick method is set inside onBindViewHolder like this:

    val isExpandable:Boolean = data[position].expandable
    holder.subItem.visibility = if(isExpandable) View.VISIBLE else View.GONE

    holder.mainItem.setOnClickListener {
        data[position].expandable = !data[position].expandable
        notifyItemChanged(position)
    }

This work very well. But, I read some developers saying that is not good idea apply onClick methods inside onBindViewHolder, and it must be done inside ViewHolder class. As I'm using Kotlin, I applied onClick method in Koltin way, using lambda. like this:

class MyAdapter(val data:List<MyModel>,
                val listener:(MyModel)->Unit):RecyclerView.Adapter<MyAdapter.MyViewHolder>() {

    class MyViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){
        fun bind (data:MyModel,listener: (MyModel) -> Unit) = with(itemView){
           ...

            view.setOnClickListener {listener(data)}

        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder = MyViewHolder(
        LayoutInflater.from(parent.context).inflate(R.layout.card_item,parent,false)
    )

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.bind(data[position], listener)
    }


    override fun getItemCount(): Int = data.count()
}

How can I use data[position] and notifyItemChanged(position) inside onClick?

Note: with this Kotlin method, in Activity, when I set the Adapter, the data can be accessed like this:

with(my_recycler) {

            layoutManager = LinearLayoutManager(this@MainActivity, RecyclerView.VERTICAL, false)

            adapter = MyAdapter(it) {
              //data can be accessed here according item position
            }
   }

Solution

  • If you want to access properties in the ViewHolder from your adapter, you can declare the ViewHolder as an Inner class.

    inner class MyViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){
          fun bind (data:MyModel,listener: (MyModel) -> Unit) = with(itemView){
           ...
    
            view.setOnClickListener {listener(data)}
    
        }
    }
    

    I haven´t tried calling the notifyItemChanged(position) but I would say that if it is going to be inside a click listener, it shouldn't crash. If you call it from outside of the listener and inside of a function that is called upon notifyItemChanged then you probably will get a crash.

    Let me know if it works.