简体   繁体   English

如何在 RecyclerView.Adapter 中使用 ViewBinding?

[英]How to use ViewBinding in a RecyclerView.Adapter?

Can I use ViewBindings to replace findViewById in this typical RecyclerView.Adapter initialization code?我可以在这个典型的RecyclerView.Adapter初始化代码中使用 ViewBindings 替换findViewById吗? I can't set a binding val in the object as the ViewHolders are different per cell.我无法在 object 中设置binding值,因为每个单元格的 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)
        // ...
    }

What you need to do is pass the generated binding class object to the holder class constructor.您需要做的是将生成的绑定类对象传递给持有者类构造函数。 In below example, I have row_payment XML file for RecyclerView item and the generated class is RowPaymentBinding so like this在下面的示例中,我有RecyclerView项目的row_payment XML 文件,生成的类是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
            }
        }
    }

Also, make sure you pass the root view to the parent class of Viewholder like this RecyclerView.ViewHolder(itemBinding.root) by accessing the passed binding class object.此外,请确保通过访问传递的绑定类对象将根视图传递给 Viewholder 的父类,如RecyclerView.ViewHolder(itemBinding.root)

I wrote a simple and reusable one:我写了一个简单且可重用的:

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"
        }
    }

}

Attach the binding to the ViewHolder instead of the Viewbinding附加到 ViewHolder 而不是 View

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

You pass the binding, the binding passes binding.root to RecyclerView.ViewHolder(binding.root)你传递绑定,绑定将binding.root传递给RecyclerView.ViewHolder(binding.root)

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

Then access anywhere with:然后通过以下方式访问任何地方:

    holder.binding.title

You may use view-binding like this :您可以像这样使用视图绑定:

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
}

}

Here is item_hobbies.xml这是 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>

If you're ok with reflection, I have a much easier way to do this.如果你对反射没意见,我有一个更简单的方法来做到这一点。 Just call ViewGroup.toBinding() then you can get the binding object you want.只需调用 ViewGroup.toBinding() 即可获得所需的绑定对象。 But since we're talk about reflection, remember you have to modify your proguard-rule to make it work even for proguard.但是既然我们在谈论反射,请记住您必须修改您的 proguard-rule 以使其即使对 proguard 也能工作。

// 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
}

I put all this into an open source project you can take a look as well.我把所有这些都放到了一个开源项目中,你也可以看看。 Not only for Adapter usage but also include Activity and Fragment.不仅用于 Adapter 使用,还包括 Activity 和 Fragment。 And do let me know if you have any comment.如果您有任何意见,请告诉我。 Thanks.谢谢。

https://github.com/Jintin/BindingExtension https://github.com/Jintin/BindingExtension

just pass your model calss into xml and set these data into xml this code look fine and add a method where you add these data into binding like you don,t need to fine the id for this只需将您的模型 calss 传递到 xml 并将这些数据设置为 xml 这段代码看起来不错,并添加一个方法,将这些数据添加到绑定中,就像您不需要的那样,不需要为此调整 id

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

}


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

You may use data binding like this.您可以像这样使用数据绑定。

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
        }
        }

I took what Alan W. did and added Generics to it.我采用了 Alan W. 所做的并将泛型添加到其中。

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

The implementation is very easy and you avoid the casting on the adapter 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)
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在Kotlin的RecyclerView.Adapter中使用SharedPreferences? - How to use SharedPreferences in RecyclerView.Adapter in Kotlin? 如何在 RecyclerView.Adapter 中使用 onActivityResult - How to use onActivityResult in RecyclerView.Adapter 如何在 RecyclerView.Adapter 中使用共享首选项? - How to use shared preferences in RecyclerView.Adapter? 对不同的RecyclerView使用相同的RecyclerView.Adapter吗? - Use same RecyclerView.Adapter for different RecyclerView? 如何在带有接口和 Cardview 的 RecyclerView 适配器中使用 ViewBinding 在 android java 中? - How to use ViewBinding in a RecyclerView Adapter with Interface and Cardview in android java? 如何正确使用 RecyclerView.Adapter notifyItemInserted/Removed? - How to use RecyclerView.Adapter notifyItemInserted/Removed in a right way? 如何在recyclerview(RecyclerView.Adapter)中删除边框? - How to remove the border in recyclerview (RecyclerView.Adapter)? 如何使用 AsyncTask、RecyclerView 和 RecyclerView.Adapter 将手机中的图像获取到 RecyclerView? - How can I use AsyncTask, RecyclerView, and a RecyclerView.Adapter to get images from my phone into my RecyclerView? Kotlin:如何在 RecyclerView.Adapter 中调用 getSupportFragmentManager() - Kotlin: How to call getSupportFragmentManager() in RecyclerView.Adapter RecyclerView.Adapter StateRestorationPolicy 是如何工作的? - How RecyclerView.Adapter StateRestorationPolicy works?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM