簡體   English   中英

如何在 RecyclerView.Adapter 中使用 ViewBinding?

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

我可以在這個典型的RecyclerView.Adapter初始化代碼中使用 ViewBindings 替換findViewById嗎? 我無法在 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)
        // ...
    }

您需要做的是將生成的綁定類對象傳遞給持有者類構造函數。 在下面的示例中,我有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
            }
        }
    }

此外,請確保通過訪問傳遞的綁定類對象將根視圖傳遞給 Viewholder 的父類,如RecyclerView.ViewHolder(itemBinding.root)

我寫了一個簡單且可重用的:

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

}

binding附加到 ViewHolder 而不是 View

    class CardViewHolder(val binding: CardBinding) : 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)
    }

然后通過以下方式訪問任何地方:

    holder.binding.title

您可以像這樣使用視圖綁定:

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>

如果你對反射沒意見,我有一個更簡單的方法來做到這一點。 只需調用 ViewGroup.toBinding() 即可獲得所需的綁定對象。 但是既然我們在談論反射,請記住您必須修改您的 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
}

我把所有這些都放到了一個開源項目中,你也可以看看。 不僅用於 Adapter 使用,還包括 Activity 和 Fragment。 如果您有任何意見,請告訴我。 謝謝。

https://github.com/Jintin/BindingExtension

只需將您的模型 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
        }
        }

我采用了 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))
}}

實現非常簡單,您可以避免對適配器類進行強制轉換:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM