如何在 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.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
只需将您的模型 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。如果您有任何意见,请告诉我。谢谢
我采用了 并向其中添加了泛型。
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)
}
我可以使用 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.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
只需将您的模型 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。如果您有任何意见,请告诉我。谢谢
我采用了
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)
}