如何在 RecyclerView.Adapter 中使用 ViewBinding?

How to use ViewBinding in a RecyclerView.Adapter?

我可以使用 ViewBindings 替换这个典型的 RecyclerView.Adapter 初始化代码中的 findViewById 吗?我无法在对象中设置 binding val,因为每个单元格的 ViewHolder 都不同。

class CardListAdapter(private val cards: LiveData<List<Card>>) : RecyclerView.Adapter<CardListAdapter.CardViewHolder>() {

    class CardViewHolder(val cardView: View) : RecyclerView.ViewHolder(cardView)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
        val binding = CardBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return CardViewHolder(binding.root)
    }

    override fun onBindViewHolder(holder: CardViewHolder, position: Int) {
        val title = holder.cardView.findViewById<TextView>(R.id.title)
        val description = holder.cardView.findViewById<TextView>(R.id.description)
        val value = holder.cardView.findViewById<TextView>(R.id.value)
        // ...
    }

binding 附加到 ViewHolder 而不是 View

class CardViewHolder(val binding: CardBinding) : RecyclerView.ViewHolder(binding.root)

你传绑定,绑定传binding.rootRecyclerView.ViewHolder(binding.root)

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
    val binding = CardBinding.inflate(LayoutInflater.from(parent.context), parent, false)
    return CardViewHolder(binding)
}

然后访问任何地方:

holder.binding.title

只需将您的模型 calss 传递到 xml 并将这些数据设置到 xml 这段代码看起来不错,并添加一个方法,将这些数据添加到绑定中,就像您不需要调整一样这个ID

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    holder.setData(listData[position])

}


fun setData(model: ListData) {
        with(binding) {
            data = model
            executePendingBindings()
        }
    }

您可以像这样使用数据绑定。

class CardListAdapter(
        private val mActivity: FragmentActivity?
    ) :
        RecyclerView.Adapter<RecyclerView.ViewHolder>() {
         private var mCustomLayoutBinding: CustomLayoutBinding? = null

          inner class MyViewHolder(val mBinding: CustomLayoutBinding) :
            RecyclerView.ViewHolder(mBinding.getRoot())
     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
            if (layoutInflater == null)
                layoutInflater = LayoutInflater.from(parent.context)

            var viewHolder: RecyclerView.ViewHolder? = null
            val inflater = LayoutInflater.from(parent.context)

       viewHolder = getViewHolder(parent, inflater)
                 return viewHolder!!
            }
            private fun getViewHolder(
            parent: ViewGroup,
            inflater: LayoutInflater
        ): RecyclerView.ViewHolder {
            mCustomLayoutBinding =
                DataBindingUtil.inflate(inflater, R.layout.custom_layout, parent, false)
            return MyViewHolder(this!!.mAssistanceLogCustomLayoutBinding!!)
        }
          override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
            val taskModal = mArrayList.get(position)
             holder.mBinding.txtTitle.setText(taskModal.title)
              }
              override fun getItemCount(): Int {
            return assistanceArrayList.size
        }

        override fun getItemId(position: Int): Long {
            return position.toLong()
        }

        override fun getItemViewType(position: Int): Int {
            return position
        }
        }

您需要做的是将生成的绑定 class 对象传递给持有者 class 构造函数。在下面的示例中,我有 row_payment XML 文件用于 RecyclerView 项目并且生成的 class 是 RowPaymentBinding 所以像这样

    class PaymentAdapter(private val paymentList: List<PaymentBean>) : RecyclerView.Adapter<PaymentAdapter.PaymentHolder>() {
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PaymentHolder {
            val itemBinding = RowPaymentBinding.inflate(LayoutInflater.from(parent.context), parent, false)
            return PaymentHolder(itemBinding)
        }
    
        override fun onBindViewHolder(holder: PaymentHolder, position: Int) {
            val paymentBean: PaymentBean = paymentList[position]
            holder.bind(paymentBean)
        }
    
        override fun getItemCount(): Int = paymentList.size
    
        class PaymentHolder(private val itemBinding: RowPaymentBinding) : RecyclerView.ViewHolder(itemBinding.root) {
            fun bind(paymentBean: PaymentBean) {
                itemBinding.tvPaymentInvoiceNumber.text = paymentBean.invoiceNumber
                itemBinding.tvPaymentAmount.text = paymentBean.totalAmount
            }
        }
    }

此外,请确保通过访问传递的绑定 class 对象,像这样 RecyclerView.ViewHolder(itemBinding.root) 将根视图传递给 Viewholder 的父视图 class。

您可以像这样使用视图绑定:

package com.example.kotlinprogramming.adapter

import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.kotlinprogramming.data.HobbiesData
import com.example.kotlinprogramming.databinding.ItemHobbieBinding

class HobbiesAdapter(var context: Context, var hobbiesList: List<HobbiesData>) :
RecyclerView.Adapter<HobbiesAdapter.HobbiesViewHolder>() {


override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HobbiesViewHolder {
    val view = ItemHobbieBinding.inflate(LayoutInflater.from(context) , parent,false)
    return HobbiesViewHolder(view)
}

override fun onBindViewHolder(holder: HobbiesViewHolder, position: Int) {
    val hobbie = hobbiesList.get(position)
    holder.viewBinding.tvHobbie.text = hobbie.title
}

inner class HobbiesViewHolder(var viewBinding: ItemHobbieBinding) : RecyclerView.ViewHolder(viewBinding.root) {
}

override fun getItemCount(): Int {
    return hobbiesList.size
}

}

这里是item_hobbies.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_margin="12dp"
android:layout_height="wrap_content"
>
<TextView
    android:id="@+id/tvHobbie"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="8dp"
    android:gravity="center"
    android:textSize="30sp"
    tools:text="Hobbie1"
    />
</androidx.cardview.widget.CardView>

我写了一个简单且可重复使用的:

class ViewBindingVH constructor(val binding: ViewBinding) :
    RecyclerView.ViewHolder(binding.root) {
    
    companion object {
        inline fun create(
            parent: ViewGroup,
            crossinline block: (inflater: LayoutInflater, container: ViewGroup, attach: Boolean) -> ViewBinding
        ) = ViewBindingVH(block(LayoutInflater.from(parent.context), parent, false))
    }
}


class CardAdapter : RecyclerView.Adapter<ViewBindingVH>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewBindingVH {
        return ViewBindingVH.create(parent, CardBinding::inflate)
    }

    override fun onBindViewHolder(holder: ViewBindingVH, position: Int) {
        (holder.binding as CardBinding).apply {
            //bind model to view
            title.text = "some text"
            descripiton.text = "some text"
        }
    }

}

如果你不介意反思,我有一个更简单的方法来做到这一点。 只要调用 ViewGroup.toBinding() 就可以得到你想要的绑定对象。 但是由于我们讨论的是反射,请记住您必须修改您的混淆器规则以使其即使对混淆器也能正常工作。

// inside adapter
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    return MainViewHolder(parent.toBinding())
}

// ViewHolder
class MainViewHolder(private val binding: AdapterMainBinding) :
    RecyclerView.ViewHolder(binding.root) {

    fun bind(data: String) {
        binding.name.text = data
    }
}

// The magic reflection can reused everywhere.
inline fun <reified V : ViewBinding> ViewGroup.toBinding(): V {
    return V::class.java.getMethod(
        "inflate",
        LayoutInflater::class.java,
        ViewGroup::class.java,
        Boolean::class.java
    ).invoke(null, LayoutInflater.from(context), this, false) as V
}

我把这些都放到了一个开源项目中,你也可以看看。 不仅用于 Adapter 使用,还包括 Activity 和 Fragment。如果您有任何意见,请告诉我。谢谢

https://github.com/Jintin/BindingExtension

我采用了 并向其中添加了泛型。

class ViewBindingVH <VB: ViewBinding> constructor(val binding: VB) :
RecyclerView.ViewHolder(binding.root) {

companion object {
    inline fun <VB: ViewBinding> create(
        parent: ViewGroup,
        crossinline block: (inflater: LayoutInflater, container: ViewGroup, attach: Boolean) -> VB
    ) = ViewBindingVH<VB>(block(LayoutInflater.from(parent.context), parent, false))
}}

实施非常简单,您可以避免在适配器上进行强制转换 class:

class PlayerViewHolder : ListAdapter<Rate, ViewBindingVH<ChartItemBinding>>(RateDiff) {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewBindingVH<ChartItemBinding> =
    ViewBindingVH.create(parent, ChartItemBinding::inflate)


override fun onBindViewHolder(holder: ViewBindingVH<ChartItemBinding>, position: Int) {
    val item = currentList[position]
    holder.binding.apply {
        
    }
}}

object RateDiff: DiffUtil.ItemCallback<Rate>(){
override fun areContentsTheSame(oldItem: Rate, newItem: Rate): Boolean {
    return oldItem == newItem
}
override fun areItemsTheSame(oldItem: Rate, newItem: Rate): Boolean {
    return oldItem == newItem
}}
abstract class BaseRecyclerViewAdapter<T : Any, VB : ViewBinding>(
     private var dataList: ArrayList<T>) 
: RecyclerView.Adapter<BaseRecyclerViewAdapter.MyViewViewHolder<VB>>() 
{   
      protected var bindingInterface: GenericSimpleRecyclerBindingInterface<T, VB>? = null

   class MyViewViewHolder<VB : ViewBinding>(val viewBinding: VB) :
    RecyclerView.ViewHolder(viewBinding.root) {
        fun <T : Any> bind(
            item: T,
            position: Int,
            bindingInterface: GenericSimpleRecyclerBindingInterface<T, VB>
            ) = bindingInterface.bindData(item, position, viewBinding)
        }

    @SuppressLint("NotifyDataSetChanged")
    fun updateList(list: ArrayList<T>) {
       dataList.clear()
       dataList.addAll(list)
       notifyDataSetChanged()
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):MyViewViewHolder<VB> {
       val view = inflateView(LayoutInflater.from(parent.context))
       return MyViewViewHolder(view)
    }

    override fun onBindViewHolder(holder: MyViewViewHolder<VB>, position: Int) {
       val item = dataList[position]
       holder.bind(item, position, bindingInterface!!)
    }

    override fun getItemCount(): Int = dataList.size

    abstract fun inflateView(inflater: LayoutInflater): VB
}

interface GenericSimpleRecyclerBindingInterface<T : Any, VB : ViewBinding> {
     fun bindData(item: T, position: Int, viewBinding: VB)
}