【问题标题】:Proper Way of using Multiple Layout Binding in Recyclerview Kotlin在 Recyclerview Kotlin 中使用多布局绑定的正确方法
【发布时间】:2021-09-02 14:08:24
【问题描述】:

嘿,我在 Recyclerview 中有多个布局。我想更改为视图绑定。我有多个布局,并且在所有布局中都有相同的 id,唯一的区别是位置不同。那么我怎样才能为此制作视图持有人。我想避免多个视图持有者。我试过不想用这个Multiple View Holder 有没有可能这样做?因为viewholder中的所有代码都是一样的。谢谢

AdapterClass.kt

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView


class AdapterClass(private val horizontal: Boolean = false) : RecyclerView.Adapter<AdapterViewHolder>() {


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AdapterViewHolder {
        val inflatedView: View = if (horizontal) {
            LayoutInflater.from(parent.context).inflate(R.layout.horizontal_layout, parent, false)
        } else {
            LayoutInflater.from(parent.context).inflate(R.layout.vertical_layout, parent, false)
        }

        return AdapterViewHolder(inflatedView)
    }

    override fun onBindViewHolder(holder: AdapterViewHolder, position: Int) {
            holder.bingImage(position)
        }
    }
    .........
}

AdapterViewHolder.kt

import android.graphics.drawable.Drawable
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource

class AdapterViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

    fun bingImage(position: Int) {
        with(itemView) {
            val color = if (isSelected) {
                itemView.context.resources.getColor(R.color.blue)
            } else {
                itemView.context.resources.getColor(R.color.green)
            }
            main_container.setBackgroundColor(color)
        }
    }
}

Vertical.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/imageView"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

Horizo​​ntal.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/imageView"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

【问题讨论】:

    标签: android kotlin generics android-recyclerview android-viewbinding


    【解决方案1】:

    使用一个ViewHolder 没有合适的方法来做到这一点,最好的解决方案是为每个不同的ViewHolder 创建多个 ViewHolder,因为这是 ViewHolder 模式的核心优势。

    但是,如果您坚持使用一个 ViewHolder,我想出了一个解决方案,但最后,您必须分别处理每个布局绑定。

    适配器

    class AdapterClass(private val horizontal: Boolean = false) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
            return if (horizontal) AdapterViewHolder.fromHorizontal(parent)
            else AdapterViewHolder.fromVertical(parent)
        }
    
        override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
            (holder as AdapterViewHolder<*>).bingImage(position)
        }
    
        ......
    }
    

    ViewHolder

    class AdapterViewHolder<T : ViewBinding> private constructor(val binding: T) : RecyclerView.ViewHolder(binding.root) {
    
        fun bingImage(position: Int) {
            with(binding.root) {
                val color = if (isSelected) {
                    context.resources.getColor(R.color.black)
                } else {
                    context.resources.getColor(R.color.green)
                }
                if (binding is HorizontalBinding)
                    binding.mainContainer.setBackgroundColor(color)
                if (binding is VerticalBinding)
                    binding.mainContainer.setBackgroundColor(color)
            }
        }
    
        companion object {
            fun fromVertical(parent: ViewGroup): AdapterViewHolder<VerticalBinding> {
                val layoutInflater = LayoutInflater.from(parent.context)
                val binding = VerticalBinding.inflate(layoutInflater, parent, false)
                return AdapterViewHolder(binding)
            }
    
            fun fromHorizontal(parent: ViewGroup): AdapterViewHolder<HorizontalBinding> {
                val layoutInflater = LayoutInflater.from(parent.context)
                val binding = HorizontalBinding.inflate(layoutInflater, parent, false)
                return AdapterViewHolder(binding)
            }
        }
    }
    

    但请注意,这不是推荐的解决方案,使用两个单独的视图持有者将允许您分别处理它们中的每一个。


    更新

    如果您需要使用多个 ViewHolder 来实现它,您必须为每个布局创建一个单独的 ViewHolder 并在其自己的 ViewHolder 中处理所有内部交互,如下所示

    Horizo​​ntalViewHolder.kt

    class HorizontalViewHolder private constructor(val binding: HorizontalBinding) : RecyclerView.ViewHolder(binding.root) {
    
        fun bind(position: Int) {
            with(binding.root) {
                val color = if (isSelected) {
                    context.resources.getColor(R.color.black)
                } else {
                    context.resources.getColor(R.color.green)
                }
                binding.mainContainer.setBackgroundColor(color)
            }
        }
    
        companion object {
            fun from(parent: ViewGroup): HorizontalViewHolder {
                val layoutInflater = LayoutInflater.from(parent.context)
                val binding = HorizontalBinding.inflate(layoutInflater, parent, false)
                return HorizontalViewHolder(binding)
            }
        }
    }
    

    VerticalViewHolder.kt

    class VerticalViewHolder private constructor(val binding: VerticalBinding) : RecyclerView.ViewHolder(binding.root) {
    
        fun bind(position: Int) {
            with(binding.root) {
                val color = if (isSelected) {
                    context.resources.getColor(R.color.black)
                } else {
                    context.resources.getColor(R.color.green)
                }
                binding.mainContainer.setBackgroundColor(color)
            }
        }
    
        companion object {
            fun from(parent: ViewGroup): VerticalViewHolder {
                val layoutInflater = LayoutInflater.from(parent.context)
                val binding = VerticalBinding.inflate(layoutInflater, parent, false)
                return VerticalViewHolder(binding)
            }
        }
    }
    

    AdapterClass.kt

    class AdapterClass(private val horizontal: Boolean = false) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
            return if (horizontal) HorizontalViewHolder.from(parent)
            else VerticalViewHolder.from(parent)
        }
    
        override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
            when(holder){
                is HorizontalViewHolder -> holder.bind(position)
                is VerticalViewHolder -> holder.bind(position)
            }
        }
    
        ......
    }
    

    这将使您能够灵活地在单独的ViewHolder 中处理每个布局的交互。

    【讨论】:

    • 嘿@HamzaSharuf 感谢您的帮助。这样做的正确方法是什么?
    【解决方案2】:

    我使用import androidx.recyclerview.widget.ListAdapter 而不是RecyclerView.Adapter,因为它比它有很多优点。

    我从这里学到的,Recycler View Codelab

    这是我使用 RecyclerView 的方式,建议您使用 2 个不同的 ViewHolders。

    示例:

    
    private val ITEM_VIEW_TYPE_HORIZONTAL = 123
    private val ITEM_VIEW_TYPE_VERTICAL = 456
    
    /**
     * Custom Data Class for this adapter
     */
    data class YourData(
        val id: Long,
        val horizontal: Boolean,
        val data: Int
    )
    
    class YourAdapter(val clickListener: DataClickListener) :
        ListAdapter<DataItem, RecyclerView.ViewHolder>(ListCheckDiffCallback()) {
    
        /**
         * This Function will help you out in choosing whether you want vertical or horizontal VIEW TYPE
         */
        override fun getItemViewType(position: Int): Int {
            return when (getItem(position).type) {
                true -> ITEM_VIEW_TYPE_HORIZONTAL
                false -> ITEM_VIEW_TYPE_VERTICAL
            }
        }
    
        /**
         * The View Type Selected above will help this function in choosing appropriate ViewHolder
         */
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
            return when (viewType) {
                ITEM_VIEW_TYPE_HORIZONTAL -> HorizontalViewHolder.from(parent)
                ITEM_VIEW_TYPE_VERTICAL -> VerticalViewHolder.from(parent)
                else -> throw ClassCastException("Unknown viewType $viewType")
            }
        }
    
        /**
         * The View Holder Created above are used here.
         */
        override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
            when (holder) {
                is HorizontalViewHolder -> {
                    val item = getItem(position) as DataItem.HorizontalClass
                    holder.bind(item.yourData, clickListener)
                }
                is VerticalViewHolder -> {
                    val item = getItem(position) as DataItem.VerticalClass
                    holder.bind(item.yourData, clickListener)
                }
            }
        }
    
        /**
         * Vertical View Holder Class
         */
        class VerticalViewHolder private constructor(val binding: <REPLACE_WITH_BINDING_OBJECT>) :
            RecyclerView.ViewHolder(binding.root) {
    
            fun bind(item: YourData, clickListener: DataClickListener) {
                /**
                 * change all your view data here
                 * assign click listeners here
                 *
                 *  example -->
                 *
                 *  binding.xyz.setOnClickListener {
                 *     clickListener.onClick(item)
                 *  }
                 */
    
            }
    
            companion object {
                fun from(parent: ViewGroup): VerticalViewHolder {
                    val layoutInflater = LayoutInflater.from(parent.context)
                    val view =  <REPLACE_WITH_BINDING_OBJECT>.inflate(R.layout.header, parent, false)
                    return VerticalViewHolder(binding)
                }
            }
        }
    
        /**
         * Horizontal View Holder
         */
        class HorizontalViewHolder private constructor(val binding: <REPLACE_WITH_BINDING_OBJECT>) :
            RecyclerView.ViewHolder(binding.root) {
    
            fun bind(item: YourData, clickListener: DataClickListener) {
                /**
                 * change all your view data here
                 * assign click listeners here
                 *
                 *  example -->
                 *
                 *  binding.xyz.setOnClickListener {
                 *     clickListener.onClick(item)
                 *  }
                 */
            }
    
            companion object {
                fun from(parent: ViewGroup): HorizontalViewHolder {
                    val layoutInflater = LayoutInflater.from(parent.context)
    
                    val binding =  <REPLACE_WITH_BINDING_OBJECT>.inflate(layoutInflater, parent, false)
                    return HorizontalViewHolder(binding)
                }
            }
        }
    }
    
    /**
     * This function checks the difference between 2 different Lists.
     * 1. Old List
     * 2. New List
     */
    class ListCheckDiffCallback : DiffUtil.ItemCallback<DataItem>() {
        override fun areItemsTheSame(oldItem: DataItem, newItem: DataItem): Boolean {
            return oldItem.id == newItem.id
        }
    
        override fun areContentsTheSame(oldItem: DataItem, newItem: DataItem): Boolean {
            return oldItem == newItem
        }
    }
    
    /**
     * Interface that can be called as per your wish.
     * I usually assign it inside the Fragment/Activity from where I am using the above Adapter.
     * like 
     *class MyFragment : Fragment(), DataClickListener
     */
    interface DataClickListener {
        fun onClick(data: YourData)
    }
    
    /**
     * Your DataItem Class
     */
    sealed class DataItem {
        data class HorizontalClass(val yourData: YourData) : DataItem() {
            override val id = yourData.id
            override val type = true
        }
    
        data class VerticalClass(val yourData: YourData) : DataItem() {
            override val id = yourData.id
            override val type = false
        }
    
        abstract val id: Long
        abstract val type: Boolean
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-21
      • 1970-01-01
      相关资源
      最近更新 更多