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 .
Table of Contents
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.