📋 Screens, Lists & Navigation · Lesson 22
RecyclerView (Lists)
Why RecyclerView?
Almost every app shows a list: chats, products, notes. If you created one view per item for 1,000 items, the phone would run out of memory. RecyclerView creates only enough rows to fill the screen and recycles them as you scroll.
The 4 parts
Step 1: Data class
Kotlin
// Subject.kt
data class Subject(val name: String, val teacher: String, val emoji: String, val chapters: Int)
val subjects = listOf(
Subject("Maths", "Mr. Gupta", "📐", 14),
Subject("Physics", "Ms. Khan", "🔭", 12),
Subject("Chemistry", "Mr. Rao", "⚗️", 10),
Subject("Biology", "Dr. Mehta", "🧬", 16),
Subject("English", "Ms. Thomas", "📖", 9),
Subject("Computer", "Mr. Singh", "💻", 11)
)Step 2: Row layout
XML – res/layout/item_subject_row.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- res/layout/item_subject_row.xml – ONE row -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="16dp">
<TextView
android:id="@+id/tvEmoji"
android:layout_width="48dp"
android:layout_height="48dp"
android:gravity="center"
android:textSize="26sp" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="17sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvTeacher"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<TextView
android:id="@+id/tvChapters"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>Step 3: Adapter + ViewHolder
Kotlin – SubjectAdapter.kt
// SubjectAdapter.kt
class SubjectAdapter(private val items: List<Subject>) :
RecyclerView.Adapter<SubjectAdapter.SubjectViewHolder>() {
// ViewHolder = holds the views of ONE row (so we don't look them up again)
class SubjectViewHolder(val binding: ItemSubjectRowBinding) : RecyclerView.ViewHolder(binding.root)
// 1. called only a few times: create a new row view
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SubjectViewHolder {
val binding = ItemSubjectRowBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return SubjectViewHolder(binding)
}
// 2. called many times: put the data of position X into a row
override fun onBindViewHolder(holder: SubjectViewHolder, position: Int) {
val s = items[position]
holder.binding.tvEmoji.text = s.emoji
holder.binding.tvName.text = s.name
holder.binding.tvTeacher.text = s.teacher
holder.binding.tvChapters.text = "${s.chapters} ch."
}
// 3. how many rows?
override fun getItemCount() = items.size
}Step 4: Add it to the screen
XML
<!-- activity_main.xml -->
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_subject_row" />Kotlin – MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.recyclerView.layoutManager = LinearLayoutManager(this)
binding.recyclerView.adapter = SubjectAdapter(subjects)
binding.recyclerView.addItemDecoration(
DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
)
}
}How recycling works
Updating the list
Kotlin
class SubjectAdapter(private val items: MutableList<Subject>) : RecyclerView.Adapter<...>() {
fun add(subject: Subject) {
items.add(0, subject)
notifyItemInserted(0) // animate just the new row
}
fun removeAt(position: Int) {
items.removeAt(position)
notifyItemRemoved(position)
}
}
// notifyDataSetChanged() redraws everything – avoid it; use ListAdapter + DiffUtil (next lesson)Common mistakes
- No LayoutManager → list stays empty with no error.
- Row root with
match_parentheight → each row fills the whole screen. Usewrap_content. - Inflating with
attachToRoot = true→ crash "child already has a parent". Always passfalse. - Putting a click listener logic that depends on the old position → use
holder.bindingAdapterPosition.
Summary
- Data class + row XML + Adapter (create, bind, count) + LayoutManager.
- ViewHolder keeps references; views are recycled while scrolling.
- Use notifyItemInserted/Removed (or ListAdapter) instead of notifyDataSetChanged.
✅ Practice
Show a list of 20 Indian states with their capital and an emoji flag-like icon. Add a FAB that inserts a new state at the top with animation.
Frequently Asked Questions
Why not use ListView?
ListView is older and less flexible. RecyclerView forces the ViewHolder pattern, supports grids, animations, item decorations and DiffUtil, and is what modern apps use.
What does a LayoutManager do?
It decides how rows are arranged: LinearLayoutManager (vertical or horizontal list), GridLayoutManager (grid) or StaggeredGridLayoutManager (Pinterest-style).
Why does my RecyclerView show nothing?
Usually the LayoutManager is missing, the list is empty, getItemCount returns 0, or the RecyclerView height is 0 because of wrap_content inside a scroll view.
