如何使用 Kotlin 在 RecyclerView Adapter 中实现 onClick 并进行数据绑定

Posted

技术标签:

【中文标题】如何使用 Kotlin 在 RecyclerView Adapter 中实现 onClick 并进行数据绑定【英文标题】:How to implement onClick in RecyclerView Adapter with databinding using Kotlin 【发布时间】:2021-07-07 07:40:38 【问题描述】:

我使用数据绑定创建了一个 RecyclerView 适配器,我正在尝试处理此适配器内的 Click 事件,但我不知道具体如何。我找到了不同的主题,但还不能解决我的问题。谢谢! 我应该在哪里(在 Adapter 类中)实现 onClick 函数?

适配器类

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

Fragment 类中的回调接口:

interface GroupContactsCallback 
   fun selectContact(contact: ContactModel)

编辑

我更新了 RemovableContactsViewHolder 中的绑定函数,但仍然无法正常工作。

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

【问题讨论】:

似乎很常见的问题,看看这个medium.com/android-gate/… 我阅读了这篇文章并在 ViewHolder 类的绑定函数中添加了 onClickListener,但仍然无法正常工作。我更新了问题,请看一下。谢谢! 【参考方案1】:

在适配器内部处理点击不是一个好习惯。并且不要仅仅为此使用界面。改用这个。

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)
           
        
    

在您的片段/活动中

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

【讨论】:

好答案,但我还要解释为什么在适配器内处理点击是不好的做法 我有那个回调作为适配器的参数,使用它不是更好吗?谢谢 嗯,它打破了“关注点分离”的概念。适配器关注的只是充当我们数据的桥梁,而不是处理点击的recyclerview。将其传递回片段/活动可以恢复它。其次,使用回调可能会导致内存泄漏,因为它会引用片段/活动。 @Razvan22 回调并不理想。我们显然可以使用它,但可能会发生内存泄漏。这也是处理点击的一种非常干净的方式。【参考方案2】:
override fun onBindViewHolder(holder: RemovableContactsViewHolder, position: Int) 
holder.bind(viewModel, callback, position)

holder.binding.your_view_like_button.setOnClickListener 
// do something 


例如

  class AppointTimeSloatAdapter(
      private var mOptionList: List<String>,
      var context: Context,
      var appointmentDate: String,
  ) : RecyclerView.Adapter<AppointTimeSloatAdapter.ViewHolder>() 
      private var checkedRadioButton: CompoundButton? = null

      override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder 
          val viewProductCategoryBinding: AdapterAppointmentTimeSloteBinding =
              AdapterAppointmentTimeSloteBinding.inflate(
                  LayoutInflater.from(parent.context),
                  parent,
                  false
              )
          return ViewHolder(viewProductCategoryBinding)
      


      override fun onBindViewHolder(holder: ViewHolder, position: Int) 
          val option = getItem(position)
          holder.binding.radioBtn.text = option.toString()

          holder.binding.radioBtn.setOnCheckedChangeListener(checkedChangeListener)
          if (holder.binding.radioBtn.isChecked) checkedRadioButton = holder.binding.radioBtn

          holder.binding.radioBtn.setOnClickListener 
              var intent = Intent(context, NewAppointmentActivity::class.java)
              intent.putExtra("time",holder.binding.radioBtn.text.toString())
              intent.putExtra("appointmentDate", appointmentDate)
              context.startActivity(intent)
          


      

      private val checkedChangeListener =
          CompoundButton.OnCheckedChangeListener  compoundButton, isChecked ->
              checkedRadioButton?.apply  setChecked(!isChecked) 
              checkedRadioButton = compoundButton.apply  setChecked(isChecked) 
          

      fun updateList(list: ArrayList<String>) 
          mOptionList = list
          notifyDataSetChanged()
      


      private fun getItem(index: Int): String 
          return mOptionList[index]
      

      override fun getItemCount(): Int 
          return mOptionList.size
      


      inner class ViewHolder(val binding: AdapterAppointmentTimeSloteBinding) :
          RecyclerView.ViewHolder(binding.root), View.OnClickListener 
          override fun onClick(v: View?) 
              ////
          
      

  

【讨论】:

【参考方案3】:

应用以下方法。

在您的适配器类中

class RemovableContactsAdapter(val viewModel: GroupDetailsViewModel,val callback:GroupContactsCallback) : RecyclerView.Adapter<RemovableContactsViewHolder>() 
private var onClickListener: OnClickListener?=null
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)

holder.itemView.setOnClickListener 
            if(onClickListener!=null)
                onClickListener!!.onClick(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()
   

 fun setOnClickListener(onClickListener: OnClickListener)
        this.onClickListener=onClickListener
    

    interface OnClickListener
        fun onClick(position: Int)
    

在你的 Activity/Fragment 类中创建一个名为 adapter 的对象,然后

val adapter=RemovableContactsAdapter(viewModel,callback)
adapter.setOnClickListener(object :SettingAdapter.OnClickListener
            override fun onClick(position: Int)
                //position is your clicked view position
            
        )

我已经在我的回答中修改了你的代码,希望问题能得到解决。

【讨论】:

以上是关于如何使用 Kotlin 在 RecyclerView Adapter 中实现 onClick 并进行数据绑定的主要内容,如果未能解决你的问题,请参考以下文章

如何在 kotlin 中使用片段

如何在 Kotlin 中使用 ViewModelProviders

如何在 Kotlin Multiplatform(纯 kotlin)中进行延迟

如何在Kotlin中使用Gradle构建缓存?

如何在 Kotlin/Multiplatform 项目中使用 .klib 库

如何使用 Kotlin 在所有活动中播放背景音乐?