在 Recyclerview Kotlin 中使用多布局绑定的正确方法
Proper Way of using Multiple Layout Binding in Recyclerview Kotlin
嘿,我在 Recyclerview 中有多个布局。我想更改为视图绑定。我有多个布局,内部在所有布局中都有相同的 id,唯一的区别是位置不同。那么我怎样才能为此制作视图持有人。我想避免使用多个视图持有者。我试过不想使用这个 有没有可能这样做?因为所有代码在 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>
Horizontal.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>
使用一个 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
中处理所有内部交互,如下所示
HorizontalViewHolder.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
中处理每个布局的交互。
我用 import androidx.recyclerview.widget.ListAdapter
而不是 RecyclerView.Adapter
因为它有很多优点。
从这里学来的,Recycler View Codelab
这就是我使用 RecyclerView 的方式,建议您使用 2 个不同的 ViewHolder。
示例:
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
}
嘿,我在 Recyclerview 中有多个布局。我想更改为视图绑定。我有多个布局,内部在所有布局中都有相同的 id,唯一的区别是位置不同。那么我怎样才能为此制作视图持有人。我想避免使用多个视图持有者。我试过不想使用这个
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>
Horizontal.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>
使用一个 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
中处理所有内部交互,如下所示
HorizontalViewHolder.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
中处理每个布局的交互。
我用 import androidx.recyclerview.widget.ListAdapter
而不是 RecyclerView.Adapter
因为它有很多优点。
从这里学来的,Recycler View Codelab
这就是我使用 RecyclerView 的方式,建议您使用 2 个不同的 ViewHolder。
示例:
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
}