Search code examples
androidkotlinandroid-recyclerviewadapterandroid-databinding

How to implement onClick in RecyclerView Adapter with databinding using Kotlin


I created a RecyclerView Adapter using databinding and I'm trying to handle Click events inside this adapter but I don't know exactly how. I found different topics but couldn't solve my problem yet. Thanks! Where should I implement (inside the Adapter class) the onClick function?

Adapter Class

class RemovableContactsAdapter(val viewModel: GroupDetailsViewModel,val callback:GroupContactsCallback) : RecyclerView.Adapter<RemovableContactsViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RemovableContactsViewHolder {
    val binding: ViewDataBinding =
        DataBindingUtil.inflate(LayoutInflater.from(parent.context), viewType, parent, false)
    return RemovableContactsViewHolder(binding)
}

override fun onBindViewHolder(holder: RemovableContactsViewHolder, position: Int) {
    holder.bind(viewModel, callback, position)

}

override fun getItemCount(): Int {
    return viewModel.currentGroup.value?.contacts?.size ?: 0
}

override fun getItemViewType(position: Int): Int {
    return R.layout.removable_contact_layout
}
}

class RemovableContactsViewHolder(val binding: ViewDataBinding) :
RecyclerView.ViewHolder(binding.root) {

fun bind(viewModel: GroupDetailsViewModel, callback: GroupContactsCallback, position: Int) {
    binding.root.selected_contact_iv.apply {
        visibility = if (viewModel.currentGroup.value?.contacts!![position].selected) {
            View.VISIBLE
        } else {
            View.GONE
        }
    }
    binding.setVariable(BR.contact, viewModel.currentGroup.value?.contacts?.get(position))
    binding.setVariable(BR.callback, callback)
    binding.executePendingBindings()
   }
}

Callback interface in Fragment class:

interface GroupContactsCallback {
   fun selectContact(contact: ContactModel)
}

EDIT

I updated the bind function inside RemovableContactsViewHolder but still not working.

class RemovableContactsViewHolder(val binding: ViewDataBinding) :
RecyclerView.ViewHolder(binding.root) {

fun bind(viewModel: GroupDetailsViewModel, callback: GroupContactsCallback, position: Int) {
    itemView.setOnClickListener {
        viewModel.currentGroup.value?.contacts?.get(position)!!.selected = !viewModel.currentGroup.value?.contacts?.get(position)!!.selected
        callback.selectContact(viewModel.currentGroup.value?.contacts?.get(position)!!)
    }

    binding.root.selected_contact_iv.apply {
        visibility = if (viewModel.currentGroup.value?.contacts!![position].selected) {
            View.VISIBLE
        } else {
            View.GONE
        }
    }

    binding.setVariable(BR.contact, viewModel.currentGroup.value?.contacts?.get(position))
    binding.setVariable(BR.callback, callback)
    binding.executePendingBindings()
}

Solution

  • It is not a good practice to handle click inside your adapter. And don't use interface just for this. Use this instead.

    class YourRecyclerViewAdapter(private val onSelect: (YourDataType?) -> Unit) : RecyclerView.Adapter<YourRecyclerViewAdapter.YourViewHolder>() {
    
        override fun onBindViewHolder(holder: YourViewHolder, position: Int) {
            holder.bind(getItem(position), onSelect)
        }
    
        class ViewHolder(private val binding: YourViewBinding) : RecyclerView.ViewHolder(binding.root) {
    
            fun bind(yourDataType: YourDataType?, onSelect: (YourDataType?) -> Unit) {
    
               // bind your view here
               binding.root.setOnClickListener {
                   onSelect(yourDataType)
               }
            }
        }
    }
    

    In your Fragment/Activity

    // Set this adapter to your recycler view
    val adapter = YourRecyclerViewAdapter { yourDataType-> 
        // Handle click here
    }