Creating RecyclerView with Multiple View Type in Android

Photo by Tam Warner Minton on Unsplash
Photo by Tam Warner Minton on Unsplash
When each row in a list is different, we need to create a ViewHolder for each row. Let’s see how to create RecyclerView with multiple ViewHolders.

When developing Android Apps, we generally use RecyclerView to draw lists. When each row in a list is different, we need to create a ViewHolder for each row. Let’s see how to create RecyclerView with multiple ViewHolders.

The complete code can be found in .

Creating a Project

Create an Empty Activity project. After that, we will draw a list with RecyclerView on MainActivity. There will be 3 different row in the list.

Understanding RecyclerView

We assume that you already know how to use RecyclerView to draw a list with a single ViewHolder. If you haven’t used RecyclerView, or are not familiar with it, you can refer to the following article first.

RecyclerView.Adapter

Add RowData, the code is as follows. It is a common interface for all kinds of rows. The methods and so on inside will be explained together with the adapter.

package com.waynestalk.androidrecyclerviewmultipleitemsexample

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

interface RowData {
    val layout: Int
    fun onCreateViewHolder(view: View): RecyclerView.ViewHolder
    fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder)
}

Add RowAdapter, its code is as follows.

package com.waynestalk.androidrecyclerviewmultipleitemsexample

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

class RowAdapter(private val list: List<RowData>) :
    RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val rowData = list.find { it.layout == viewType }!!
        val inflater = LayoutInflater.from(parent.context)
        val view = inflater.inflate(viewType, parent, false)
        return rowData.onCreateViewHolder(view)
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val rowData = list[position]
        rowData.onBindViewHolder(holder)
    }

    override fun getItemViewType(position: Int): Int {
        return list[position].layout
    }

    override fun getItemCount(): Int {
        return list.size
    }
}

RowAdapter inherits RecyclerView.Adapter and receives a List<RowData> as a parameter in the constructor.

  • getItemCount(): Returns the total number of rows.
  • getItemViewType(): ItemViewType refers to the type of the current row (item view). Therefore, there will be as many types as there are item views. The layout is returned here, because it happens that the type of layout is Int, and each item view has its own layout.
  • onCreateViewHolder(): In this method, viewType is actually layout. Find the RowData that uses the same layout, call RowData.onCreateViewHolder(), and create a ViewHolder there.
  • onBindViewHolder(): In this method, call RowData.onBindViewHolder() to bind data.

Creating Multiple ViewHolders

Next, let’s create three kinds of ViewHolder to get familiar with how to use RowData to create a variety of different ViewHolder.

TextRowData

Added row_text.xml. It contains two TextViews, one displays a title and the other displays a value.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/titleTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/valueTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Added TextRowData and inherited RowData. TextRowData is quite simple, so we won’t explain it.

package com.waynestalk.androidrecyclerviewmultipleitemsexample

import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView

class TextRowData(private val title: String, private val value: String) : RowData {
    inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val titleTextView: TextView = itemView.findViewById(R.id.titleTextView)
        val valueTextView: TextView = itemView.findViewById(R.id.valueTextView)
    }

    override val layout = R.layout.row_text

    override fun onCreateViewHolder(view: View) = ViewHolder(view)

    override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder) {
        viewHolder as ViewHolder
        viewHolder.titleTextView.text = title
        viewHolder.valueTextView.text = value
    }
}

EditRowData

Added row_edit.xml. It displays a title and a text input box.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/titleTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/valueEditText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:importantForAutofill="no"
        android:inputType="textPersonName"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="LabelFor" />

</androidx.constraintlayout.widget.ConstraintLayout>

Add EditRowData. It is similar to TextRowData, but with an additional doAfterTextChanged callback. When the text in the input box is changed, the callback will be called.

package com.waynestalk.androidrecyclerviewmultipleitemsexample

import android.text.Editable
import android.view.View
import android.widget.EditText
import android.widget.TextView
import androidx.core.widget.doAfterTextChanged
import androidx.recyclerview.widget.RecyclerView

class EditRowData(
    private val title: String,
    private val value: String,
    private val doAfterTextChanged: (text: String) -> Unit
) : RowData {
    inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val titleTextView: TextView = itemView.findViewById(R.id.titleTextView)
        val valueEditText: EditText = itemView.findViewById(R.id.valueEditText)
    }

    override val layout = R.layout.row_edit

    override fun onCreateViewHolder(view: View) = ViewHolder(view)

    override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder) {
        viewHolder as ViewHolder
        viewHolder.titleTextView.text = title
        viewHolder.valueEditText.text = Editable.Factory.getInstance().newEditable(value)
        viewHolder.valueEditText.doAfterTextChanged { it.toString().let(doAfterTextChanged) }
    }
}

SwitchRowData

Add row_switch.xml. It displays a title, and a switch.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/titleTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.appcompat.widget.SwitchCompat
        android:id="@+id/valueSwitch"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Add SwitchRowData, which is similar to EditRowData.

package com.waynestalk.androidrecyclerviewmultipleitemsexample

import android.view.View
import android.widget.TextView
import androidx.appcompat.widget.SwitchCompat
import androidx.recyclerview.widget.RecyclerView

class SwitchRowData(
    private val title: String,
    private val value: Boolean,
    private val onCheckedChange: (Boolean) -> Unit
) : RowData {
    inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val titleTextView: TextView = itemView.findViewById(R.id.titleTextView)
        val valueSwitch: SwitchCompat = itemView.findViewById(R.id.valueSwitch)
    }

    override val layout = R.layout.row_switch

    override fun onCreateViewHolder(view: View) = ViewHolder(view)

    override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder) {
        viewHolder as ViewHolder
        viewHolder.titleTextView.text = title
        viewHolder.valueSwitch.isChecked = value
        viewHolder.valueSwitch.setOnCheckedChangeListener { buttonView, isChecked ->
            onCheckedChange(isChecked)
        }
    }
}

Use RowAdapter in MainActivity

Finally, let’s draw a list with RecyclerView in MainActivity.

Add activity_main.xml, which displays a RecyclerView.

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/mainRecyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

In MainActivity, add RowAdapter and 3 rows.

package com.waynestalk.androidrecyclerviewmultipleitemsexample

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val recyclerView = findViewById<RecyclerView>(R.id.mainRecyclerView)
        recyclerView.adapter = RowAdapter(
            listOf(
                TextRowData("Name", "Wayne's Take"),
                EditRowData("Phone Number", "12345678") { println("Phone number: $it") },
                SwitchRowData("Registered", false) { println("Registered: $it") }
            )
        )
        recyclerView.layoutManager = LinearLayoutManager(this)
    }
}

That’s it!

Conclusion

The most important thing in this article is RowData. It separates the logic and code related to each row and includes them in a class. This not only reduces the complexity of RowAdapter, the code of each file is also quite short.

Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like