DataBinding:如何使用 Kotlin 中的泛型创建具有多个 ViewHolder(多个布局)的 Recyclerview

Posted

技术标签:

【中文标题】DataBinding:如何使用 Kotlin 中的泛型创建具有多个 ViewHolder(多个布局)的 Recyclerview【英文标题】:DataBinding: How to create Recyclerview with multiple ViewHolder (multiple layout) using generics in Kotlin 【发布时间】:2021-06-19 22:23:33 【问题描述】:

我有下面的代码,我在具有多个 ViewHolders 的 RecyclerView 中使用泛型,我正在尝试使用 DataBinding 升级此代码,我正在寻找具有多个 ViewHolders 的 RecyclerView 并在其中使用 DataBinding。

class JumbledWordAdapter(
var items: List<JumbledWord>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() 

companion object 
    const val TYPE_PLACE_HOLDER = 0
    const val TYPE_ANSWER = 1
    const val INVALID = -1


var selectedItemPos = INVALID

override fun getItemViewType(position: Int) = (items[position].currentType)//choose layout type

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder 
    return when (viewType) 
        TYPE_PLACE_HOLDER -> 
            PlaceHolderViewHolder(
                LayoutInflater.from(parent.context)
                    .inflate(R.layout.lyt_jumbled_word_place_holder_item, parent, false)
            )
        
        TYPE_ANSWER -> 
            AnswerViewHolder(
                LayoutInflater.from(parent.context)
                    .inflate(R.layout.lyt_jumbled_word_answer_item, parent, false)
            )
        
        else -> throw IllegalArgumentException("Invalid view type")
    


override fun getItemCount(): Int = items.size

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) 
    when (holder) 
        is PlaceHolderViewHolder -> holder.bind(position)
        is AnswerViewHolder -> holder.bind(position)
    


inner class PlaceHolderViewHolder(val view: View) : RecyclerView.ViewHolder(view) 
    private val cardParent: MaterialCardView = view.findViewById(R.id.cardParent)
    fun bind(position: Int) 
        cardParent.isSelected = items[position].isSelected
        cardParent.setOnClickListener 
           //some code.
        
    


inner class AnswerViewHolder(val view: View) : RecyclerView.ViewHolder(view) 
    private val cardParent: MaterialCardView = view.findViewById(R.id.cardParent)
    private val tvLetter: TextView = view.findViewById(R.id.tvLetter)
    fun bind(position: Int) 
        tvLetter.text = items[position].answer
        cardParent.setOnClickListener 
           //some code.
        
    
 

需要在这个适配器类中实现Databinding。

【问题讨论】:

【参考方案1】:

我认为 DataBinding 应该在 ViewHolder 中隔离,因为将值绑定到视图不是 Adapter 的责任。

所以我写了这个:

class JumbledWordAdapter(
    val items: List<ItemListFragment.JumbledWord>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() 

    companion object 
        const val TYPE_PLACE_HOLDER = 0
        const val TYPE_ANSWER = 1
        const val INVALID = -1
    

    var selectedItemPos = INVALID

    override fun getItemViewType(position: Int) = (items[position].currentType)//choose layout type

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder 
        return when (viewType) 
            TYPE_PLACE_HOLDER -> PlaceHolderViewHolder(parent)
            TYPE_ANSWER -> AnswerViewHolder(parent)
            else -> throw IllegalArgumentException("Invalid view type")
        
    

    override fun getItemCount(): Int = items.size

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) 
        when (holder) 
            is PlaceHolderViewHolder -> holder.bind(position)
            is AnswerViewHolder -> holder.bind(position)
        
    

    inner class PlaceHolderViewHolder private constructor(
        val binding: LytJumbledWordPlaceHolderItemBinding
    ) : RecyclerView.ViewHolder(binding.root) 

        constructor(parent: ViewGroup) : this(
            LytJumbledWordPlaceHolderItemBinding.inflate(
                LayoutInflater.from(parent.context),
                parent,
                false
            )
        )

        init 
            // set click listeners at view-holder initialize time
            binding.cardParent.setOnClickListener 
                if (adapterPosition != RecyclerView.NO_POSITION) 
                    //some code.    
                
            
        

        fun bind(position: Int) 
            binding.cardParent.isSelected = items[position].isSelected
        
    

    inner class AnswerViewHolder private constructor(
        val binding: LytJumbledWordAnswerItemBinding
    ) : RecyclerView.ViewHolder(binding.root) 

        constructor(parent: ViewGroup) : this(
            LytJumbledWordAnswerItemBinding.inflate(
                LayoutInflater.from(parent.context),
                parent,
                false
            )
        )

        init 
            // set click listeners at view-holder initialize time
            binding.cardParent.setOnClickListener 
                if (adapterPosition != RecyclerView.NO_POSITION) 
                    //some code.    
                
            
        

        fun bind(position: Int) 
            binding.tvLetter.text = items[position].answer
        
    

PS:我们还应该在viewholder初始化时间设置clickListeners。 (不在绑定时间内)

【讨论】:

adapterPosition 已弃用。我可以使用 getBindingAdapterPosition() 吗? @Alfin 我正在使用最新的稳定版本的 recyclerview (1.1.0)。如果您使用的是不稳定版本,是的。你可以改用getBindingAdapterPosition()

以上是关于DataBinding:如何使用 Kotlin 中的泛型创建具有多个 ViewHolder(多个布局)的 Recyclerview的主要内容,如果未能解决你的问题,请参考以下文章

DataBinding——使用Kotlin 委托优化

DataBinding——使用Kotlin 委托优化

使用 Kotlin Multiplatform Mobile (KMM) 的多平台应用程序中的 DataBinding 错误

Kotlin之Databinding的配置和使用

Kotlin之Databinding的配置和使用

DataBinding 和 LiveData :两种实现(Kotlin 和 Java),不能使 Java impl 工作