Creating Custom RecyclerView Adapters and ViewHolders
Course: Android App Development
Topic: Creating Custom RecyclerView Adapters and ViewHolders
Course Link: Android App Development Course
Demo Link: Register for a Free Course Demo
Website: JustAcademy
1. Introduction to RecyclerView
RecyclerView is an Android UI component used to display collections of data efficiently. It is suitable for lists of contacts, products, messages, transactions, courses, notifications, and other repeated items. Instead of creating a new view for every record, RecyclerView reuses item views that move off-screen. This recycling helps reduce unnecessary view creation and improves scrolling performance.
A custom RecyclerView implementation usually includes a data model, an XML layout for each row, a ViewHolder, an Adapter, and a LayoutManager. These components work together to display data and respond to user interactions.
2. Learning Objectives
- Understand RecyclerView, Adapter, and ViewHolder responsibilities.
- Create a custom item layout using XML.
- Build a data model for list items.
- Implement
onCreateViewHolder(), onBindViewHolder(), and getItemCount().
- Handle item clicks and update list data.
- Understand view recycling and avoid common binding errors.
- Use ListAdapter and DiffUtil for efficient list updates.
3. What Is a Custom RecyclerView Adapter?
An Adapter connects a collection of data to the views displayed by RecyclerView. A custom adapter defines how each data object should appear inside an individual list item. It creates ViewHolder objects when necessary and binds the appropriate data to those holders.
For example, a shopping application may store products containing a name, price, and category. The adapter takes each product and displays its details in a product card.
Responsibilities of an Adapter
- Report how many items exist in the data set.
- Create item views when RecyclerView needs new ViewHolders.
- Bind the correct data to each ViewHolder.
- Handle or forward item interaction events.
- Notify RecyclerView when the displayed data changes.
4. What Is a ViewHolder?
A ViewHolder represents one item view in RecyclerView. It holds references to the views inside an item layout, such as TextViews, ImageViews, Buttons, and other UI elements. RecyclerView can reuse a ViewHolder for another data item when scrolling.
Rather than repeatedly searching the item layout for its child views during every bind, a ViewHolder stores those references. This separates item-view initialization from data binding and helps keep adapter code organized.
Adapter vs. ViewHolder
| Feature |
Adapter |
ViewHolder |
| Purpose |
Connects data with list item views. |
Holds references to one item's views. |
| Main responsibility |
Creates holders and binds data. |
Provides access to item UI components. |
| Important methods |
onCreateViewHolder(), onBindViewHolder(), getItemCount() |
Constructor and optional binding/helper methods. |
| Quantity |
Usually one adapter for a list. |
Multiple holders may be created and recycled. |
5. How RecyclerView Works
- Data preparation: The application provides a collection of model objects.
- Layout arrangement: A LayoutManager determines whether items appear vertically, horizontally, or in a grid.
- Holder creation: RecyclerView calls
onCreateViewHolder() when it needs a new item view.
- Data binding: RecyclerView calls
onBindViewHolder() to display the relevant record in a holder.
- Scrolling: As items leave the visible area, their views may be reused for other records.
- Data updates: Adapter notifications or a ListAdapter diff update the displayed items.
6. Required Dependency and RecyclerView Layout
In a standard Android Views project, ensure that the AndroidX RecyclerView library is available in the app module. Use the current stable version compatible with your project and Gradle setup.
dependencies {
implementation("androidx.recyclerview:recyclerview:<current-stable-version>")
}
Replace the placeholder with the appropriate stable library version. Sync the project after updating Gradle.
Add RecyclerView to an Activity or Fragment XML layout, for example activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:padding="8dp" />
7. Step One: Create a Data Model
A data model defines the information represented by each list item. In this example, each student has an ID, a name, and a course.
data class Student(
val id: Int,
val name: String,
val course: String
)
Using a model class is generally more maintainable than keeping related values in separate arrays because each student's information remains grouped in one object.
8. Step Two: Create an Item Layout
Create a layout file named item_student.xml in the application's res/layout directory. It defines the appearance of one student row.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/txtStudentName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textStyle="bold"
android:text="Student Name" />
<TextView
android:id="@+id/txtStudentCourse"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textSize="14sp"
android:text="Course Name" />
</LinearLayout>
The two TextViews display the student's name and enrolled course. You can replace this layout with a CardView or MaterialCardView when a card-style design is needed.
9. Step Three: Create a Custom ViewHolder
The ViewHolder receives the inflated item view and finds its child views once. Those references can then be reused whenever the holder is bound to another student.
class StudentViewHolder(itemView: View) :
RecyclerView.ViewHolder(itemView) {
val nameTextView: TextView =
itemView.findViewById(R.id.txtStudentName)
val courseTextView: TextView =
itemView.findViewById(R.id.txtStudentCourse)
}
Required imports include android.view.View, android.widget.TextView, and androidx.recyclerview.widget.RecyclerView.
10. Step Four: Implement the Custom Adapter
The adapter below receives a list of Student objects and binds each student's details to a ViewHolder.
class StudentAdapter(
private val students: List<Student>,
private val onStudentClick: (Student) -> Unit
) : RecyclerView.Adapter<StudentViewHolder>() {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): StudentViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_student, parent, false)
return StudentViewHolder(view)
}
override fun onBindViewHolder(
holder: StudentViewHolder,
position: Int
) {
val student = students[position]
holder.nameTextView.text = student.name
holder.courseTextView.text = student.course
holder.itemView.setOnClickListener {
onStudentClick(student)
}
}
override fun getItemCount(): Int = students.size
}
Required imports include android.view.LayoutInflater, android.view.ViewGroup, and androidx.recyclerview.widget.RecyclerView. The click callback allows the Activity or Fragment to decide what should happen when a student row is tapped.
Understanding the Three Core Adapter Methods
| Method |
Purpose |
When it is used |
onCreateViewHolder() |
Inflates the item layout and creates a ViewHolder. |
When RecyclerView needs another holder. |
onBindViewHolder() |
Places the current item's data into the holder's views. |
When an item must be displayed or rebound. |
getItemCount() |
Returns the number of items in the adapter's data set. |
Whenever RecyclerView needs the item count. |
11. Step Five: Attach the Adapter to RecyclerView
Initialize RecyclerView in an Activity after setting its content view. A LinearLayoutManager displays items in a vertical list by default.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val students = listOf(
Student(1, "Aarav Sharma", "Android Development"),
Student(2, "Meera Patel", "Java Programming"),
Student(3, "Kabir Singh", "Mobile App Design")
)
val recyclerView =
findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager =
LinearLayoutManager(this)
recyclerView.adapter = StudentAdapter(students) { student ->
Toast.makeText(
this,
"Selected: ${student.name}",
Toast.LENGTH_SHORT
).show()
}
}
}
Required imports include androidx.appcompat.app.AppCompatActivity, androidx.recyclerview.widget.LinearLayoutManager, androidx.recyclerview.widget.RecyclerView, and android.widget.Toast.
12. Handling Clicks in ViewHolders
Click behavior can be assigned to the complete item view or to a specific child view. Passing a callback into the adapter is a clean way to keep navigation and business logic outside the ViewHolder.
holder.itemView.setOnClickListener {
onStudentClick(student)
}
For a button inside each row, bind its click listener similarly:
holder.deleteButton.setOnClickListener {
onDeleteClick(student)
}
Make sure the button and callback are declared in the ViewHolder and adapter when using the second example. For operations such as deleting an item, update the underlying data set and notify the adapter appropriately.
13. Updating RecyclerView Data
A simple adapter can use a mutable list, but it must keep the data and RecyclerView notifications synchronized. For small examples, you may add or remove an item and notify the adapter:
studentsMutableList.add(newStudent)
adapter.notifyItemInserted(studentsMutableList.lastIndex)
// To remove an item:
val removedPosition = 0
studentsMutableList.removeAt(removedPosition)
adapter.notifyItemRemoved(removedPosition)
This snippet assumes that studentsMutableList is a mutable list used by the adapter and that the adapter exposes the appropriate update mechanism. Avoid modifying a separate list while the adapter continues reading a different list. Prefer explicit adapter methods or ListAdapter for larger applications.
14. Using ListAdapter and DiffUtil
ListAdapter is often preferable when lists change frequently. It uses DiffUtil to calculate differences between old and new lists and updates only the affected items. This reduces the need for manually choosing notification calls.
class StudentListAdapter(
private val onClick: (Student) -> Unit
) : ListAdapter<Student, StudentViewHolder>(StudentDiffCallback()) {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): StudentViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_student, parent, false)
return StudentViewHolder(view)
}
override fun onBindViewHolder(
holder: StudentViewHolder,
position: Int
) {
val student = getItem(position)
holder.nameTextView.text = student.name
holder.courseTextView.text = student.course
holder.itemView.setOnClickListener {
onClick(student)
}
}
}
class StudentDiffCallback : DiffUtil.ItemCallback<Student>() {
override fun areItemsTheSame(
oldItem: Student,
newItem: Student
): Boolean = oldItem.id == newItem.id
override fun areContentsTheSame(
oldItem: Student,
newItem: Student
): Boolean = oldItem == newItem
}
Import androidx.recyclerview.widget.ListAdapter and androidx.recyclerview.widget.DiffUtil. To display updated data, call adapter.submitList(updatedStudents). For safe diff calculations, submit a new list rather than modifying a list instance that is already being displayed.
15. Supporting Multiple View Types
Some lists contain different kinds of rows, such as a heading followed by products or a message followed by a date separator. In this case, override getItemViewType() and create the appropriate ViewHolder for each type.
override fun getItemViewType(position: Int): Int {
return if (items[position] is HeaderItem) {
TYPE_HEADER
} else {
TYPE_CONTENT
}
}
Define the item types as constants and implement the corresponding branches in onCreateViewHolder() and onBindViewHolder(). The example assumes a data model with HeaderItem and content item types. Do not use this isolated snippet without defining those classes and the appropriate holder types.
16. RecyclerView LayoutManagers
| LayoutManager |
Arrangement |
Common use |
LinearLayoutManager |
Vertical or horizontal list |
Contacts, messages, transaction history |
GridLayoutManager |
Regular grid |
Product catalogs, photo galleries |
StaggeredGridLayoutManager |
Grid with items of different sizes |
Masonry-style image feeds |
The adapter determines how data is bound; the LayoutManager determines how item views are positioned and recycled within the list.
17. Common Errors and Troubleshooting
| Problem |
Likely cause |
Solution |
| RecyclerView displays nothing |
Missing LayoutManager or adapter, empty data, or incorrect layout ID |
Verify the view ID, item count, LayoutManager, and adapter assignment. |
| Wrong details appear after scrolling |
Not all item views are rebound |
Set every relevant view property in the bind method. |
| App crashes during binding |
Incorrect position access or a missing view ID |
Check the data bounds and confirm the item XML IDs. |
| Rows do not refresh |
Underlying data changed without notifying RecyclerView |
Use correct notification methods or submit a new ListAdapter list. |
| Click opens the wrong record |
Click listener captures a position that later becomes stale |
Capture the bound item object or use a valid adapter position when needed. |
| Scrolling is slow |
Expensive work occurs during binding or layouts are unnecessarily complex |
Keep binding lightweight, reuse views, and optimize item layouts. |
| Images appear on the wrong rows |
Asynchronous image loading is not managed correctly |
Use an image-loading library and ensure each bind requests the correct image. |
18. Best Practices
- Keep the ViewHolder focused on references to item views and simple item-level behavior.
- Keep the adapter responsible for binding data, not for database access or network requests.
- Use data classes to represent list items clearly.
- Bind every field that can vary between items, including visibility, selected state, and image placeholders.
- Use ListAdapter and DiffUtil for frequently updated collections.
- Keep expensive calculations, database queries, and network calls out of
onBindViewHolder().
- Use View Binding when enabled in the project to reduce repetitive
findViewById() calls.
- Use stable item identifiers when the application's data model and adapter support them correctly; do not use changing positions as stable IDs.
- Provide meaningful accessibility descriptions for images and actionable controls.
- Test list behavior with empty data, many items, scrolling, and repeated updates.
19. Practical Exercise
Task: Build a student directory application using RecyclerView.
- Create a Student data class with ID, name, course, and email.
- Design an XML item layout showing the name, course, and email.
- Create a StudentViewHolder that stores references to the layout's views.
- Implement a custom adapter and bind each student to a row.
- Configure RecyclerView with LinearLayoutManager.
- Show a Toast or open a details screen when a student is selected.
- Add a search field to filter students and update the list safely.
- Optionally migrate to ListAdapter and DiffUtil to handle updates efficiently.
Expected result: A scrollable directory that displays student information correctly, responds to clicks, and updates without creating a separate view for every data item.
20. Interview Questions and Answers
Q1. What is RecyclerView?
RecyclerView is an Android UI component that efficiently displays collections by recycling item views as they move on and off the screen.
Q2. What is the purpose of an Adapter?
The Adapter connects the data set to RecyclerView by creating ViewHolders and binding data to their views.
Q3. What is a ViewHolder?
A ViewHolder represents one item view and stores references to the child views used to display that item's information.
Q4. What does onCreateViewHolder() do?
It creates a ViewHolder by inflating the appropriate item layout. It does not normally bind a specific data record.
Q5. What does onBindViewHolder() do?
It binds the data for a particular item to the views held by the ViewHolder.
Q6. Why is getItemCount() required?
It returns the number of items available in the adapter's current data set.
Q7. Why does RecyclerView recycle views?
Recycling avoids repeatedly creating views, which helps conserve resources and improves scrolling efficiency.
Q8. What is the difference between RecyclerView and ListView?
RecyclerView offers a flexible ViewHolder-based design, configurable LayoutManagers, and built-in support for item animations. ListView is an older list component with a more limited layout model.
Q9. What is DiffUtil?
DiffUtil calculates the difference between two lists so that RecyclerView can apply targeted updates instead of refreshing the entire data set unnecessarily.
Q10. Why should every item field be rebound?
Because recycled views may previously have displayed different data. Resetting all variable properties prevents stale text, images, visibility, or selection states from appearing.
21. Quick Revision
- RecyclerView: Displays and recycles item views.
- Adapter: Creates holders and connects data to UI.
- ViewHolder: Holds references to an item's views.
- onCreateViewHolder(): Creates the item view and holder.
- onBindViewHolder(): Displays the current item's data.
- getItemCount(): Returns the data set size.
- LayoutManager: Controls list or grid arrangement.
- ListAdapter and DiffUtil: Help update changing lists efficiently.
22. Official Learning Resource
Read the Android Developers guide for additional implementation details and examples: Create Dynamic Lists with RecyclerView.
Conclusion
Custom RecyclerView Adapters and ViewHolders are essential when building efficient, data-driven Android interfaces. The ViewHolder manages references to item views, while the Adapter creates holders and binds data to them. Combining a well-structured model, a reusable XML layout, a custom adapter, and an appropriate LayoutManager creates lists that are maintainable, responsive, and easy to extend.
Explore more Android App Development learning resources at JustAcademy Android App Development, register for a course demo, or visit the JustAcademy homepage.