Search code examples
androidkotlingenericsandroid-recyclerviewandroid-viewbinding

Proper Way of using Multiple Layout Binding in Recyclerview Kotlin


Hey I have multiple layout in Recyclerview. I want to change into view binding. I have multiple layout and inside that have same id in all layout, only difference is position is different. So How can i make view Holder for that. I want to avoid multiple view holder. I tried don't want to use this Multiple View Holder Is there any possibility to do that? Because all code are same in viewholder. Thanks

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>

Solution

  • There is no proper way to do that using one ViewHolder, The best solution is to create multiple ViewHolders for each different ViewHolder, because this is the core benefit of the ViewHolder pattern.

    However, If you insist to do it using one ViewHolder, I came up with a solution but at last, you will have to handle each layout bindings separately.

    Adapter

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

    But note that this is not a recommended solution and using two separate view holders will allow you to handle each of them separately.


    Update

    If you need to implement that using multiple ViewHolders you will have to create a separate ViewHolder for each layout and handle all internal interactions inside its own ViewHolderas follows

    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)
            }
        }
    
        ......
    }
    

    This will give you the flexibility to handle the interaction of each layout in a separate ViewHolder.