Module 9 of 20

RecyclerView & LazyColumn (Deep Dive)

Implement high-performance lists and grids using RecyclerView in XML and LazyColumn/LazyRow in Jetpack Compose.

Module 9: RecyclerView & LazyColumn (Deep Dive)

Learning Objectives

By the end of this module, you’ll understand:

  • Why RecyclerView exists
  • Why ListView became obsolete
  • RecyclerView architecture
  • View recycling
  • Adapter
  • ViewHolder
  • LayoutManager
  • ItemDecoration
  • DiffUtil
  • ListAdapter
  • Paging (high level)
  • LazyColumn in Compose
  • Performance optimization
  • Common interview questions

Part 1 – Why RecyclerView Exists


1. The Naive Approach

Imagine showing 10,000 contacts.

A beginner might think:

Contact 1 -> View
Contact 2 -> View
Contact 3 -> View
...
Contact 10000 -> View

That means:

10,000

  • TextViews
  • ImageViews
  • Buttons
  • Layouts

Memory usage becomes enormous.


Suppose each row consumes only 20 KB.

20 KB × 10,000

=

200 MB

And that’s only for one screen.

Impossible for a phone.


2. The Key Observation

Question:

How many items are actually visible?

Example:

Phone Screen



15 Items Visible

Not 10,000.

Only around 10–20.

So why create thousands of Views?

Create only what’s visible.

Reuse them.

This idea is recycling.


3. The Bus Analogy

Imagine a city bus.

Passengers get off.

New passengers get on.

The bus isn’t replaced.

Only the passengers change.

RecyclerView works exactly like this.

RecyclerView



View



New Data



Same View

The View survives.

Its displayed data changes.


4. ListView vs RecyclerView

Before RecyclerView, Android had ListView.

Problems:

  • Limited animations
  • Poor flexibility
  • Different APIs for grids
  • Harder customization
  • Weak update mechanisms

RecyclerView unified these ideas.


ListViewRecyclerView
Vertical list onlyMultiple layouts
Basic recyclingAdvanced recycling
No LayoutManager abstractionLayoutManager
Limited animationsRich animations
No DiffUtilEfficient updates

Part 2 – RecyclerView Architecture

RecyclerView isn’t one class.

It’s a collaboration of several components.

RecyclerView



├── Adapter

├── ViewHolder

├── LayoutManager

├── ItemAnimator

└── ItemDecoration

Each has a single responsibility.


5. RecyclerView

RecyclerView itself does very little.

It coordinates everything.

Think of it as a manager.

Responsibilities:

  • Handle scrolling
  • Ask Adapter for data
  • Recycle Views
  • Delegate positioning
  • Coordinate animations

It doesn’t know what your items represent.


6. Adapter

The Adapter is the bridge between:

Data



RecyclerView

Suppose you have:

val users = listOf(...)

RecyclerView doesn’t understand User.

Adapter does.

Its job:

  • Create item Views
  • Bind data
  • Report item count

Think of it as a translator.


7. ViewHolder

One of the most important concepts.

Imagine a row:

+-------------------------+

Avatar

Name

Last Message

+-------------------------+

Without ViewHolder:

Every scroll:

findViewById()

findViewById()

findViewById()

Repeated hundreds of times.

Expensive.

Instead:

class UserViewHolder(...)

Holds references once.

ViewHolder



Avatar



Text



Button

No repeated lookup.


8. Adapter Lifecycle

RecyclerView asks the Adapter three important questions.


getItemCount()

Question:

“How many items exist?”

Example:

override fun getItemCount() = users.size

onCreateViewHolder()

Question:

“I need a new View.”

RecyclerView:

Need New Row



Adapter



Inflate XML



Create ViewHolder

Notice:

This is NOT called for every scroll.

Only when RecyclerView needs another View.


onBindViewHolder()

Question:

“Fill this View with new data.”

Example:

Existing View



User #25



Bind



Now displays User #400

This is called frequently.

It should be fast.


9. Recycling

Imagine:

Visible:

1

2

3

4

5

Scroll.

Now:

2

3

4

5

6

Did Android create another View?

No.

Instead:

View for Item 1



Recycled



Now Displays Item 6

The object survives.

Only the content changes.


10. Recycling Pool

Internally:

RecyclerView



Recycle Pool



Unused Views

When a row disappears:

Visible



Off Screen



Recycle Pool

Later:

Need View



Take From Pool



Bind New Data

Almost no allocations.

Excellent performance.


Part 3 – LayoutManager

RecyclerView doesn’t know where items belong.

LayoutManager decides.


LinearLayoutManager

1

2

3

4

Vertical.

Or horizontal.


GridLayoutManager

1 2

3 4

5 6

Useful for:

  • Gallery
  • Shopping
  • Photos

StaggeredGridLayoutManager

Pinterest style.

■■

■■■■



■■■

Different heights.


Notice something interesting.

RecyclerView didn’t change.

Only LayoutManager changed.

That’s great architecture.


Part 4 – ItemDecoration

Suppose you want spacing.

Instead of modifying every item:

RecyclerView offers:

ItemDecoration

Responsibilities:

  • Margins
  • Dividers
  • Decorations

Keeps item layouts simpler.


Part 5 – ItemAnimator

Suppose:

User deletes item.

Without animation:

A

B

C



Delete B



A

C

Looks abrupt.

RecyclerView animates:

A

B

C



Slide



A

C

Smooth.


Part 6 – Why notifyDataSetChanged() Is Bad

Suppose:

One item changed.

Many beginners do:

notifyDataSetChanged()

RecyclerView thinks:

Everything Changed

Rebinds every visible item.

Wasteful.


Instead:

notifyItemChanged(5)

Only one row updates.

Even better:

Use DiffUtil.


Part 7 – DiffUtil

Imagine:

Old List:

A

B

C

D

New List:

A

B

E

D

Without DiffUtil:

Refresh Everything

With DiffUtil:

Only C Changed



Update Row 3

Efficient.


8. ListAdapter

Google built ListAdapter on top of DiffUtil.

Instead of manually calculating differences:

Old List



DiffUtil



Minimal Updates

Automatically.

Most modern apps use ListAdapter rather than implementing RecyclerView.Adapter directly.


Part 8 – Performance Considerations

Avoid Heavy Work in onBindViewHolder()

Bad:

Bind



Download Image



Database Query

Binding should be quick.

Heavy work belongs elsewhere (repositories, image loading libraries, background threads).


Reuse Objects

Avoid creating unnecessary allocations during binding.

Remember:

onBindViewHolder() may execute thousands of times.


Stable IDs

If items have unique identifiers:

RecyclerView can preserve animations more effectively.

Useful for complex updates.


Part 9 – Paging (High Level)

Imagine:

One million products.

Don’t load them all.

Instead:

Load 20



Scroll



Load Next 20



Scroll



Next 20

Jetpack Paging automates this pattern.

We’ll study Paging separately.


Part 10 – LazyColumn (Compose)

RecyclerView doesn’t exist in Compose.

Instead:

LazyColumn {

}

Question:

Does Compose create all rows?

No.

Exactly like RecyclerView:

Visible Items



Compose



Dispose Invisible



Compose New Ones

Same concept.

Different implementation.


Example:

LazyColumn {
    items(users) { user ->
        UserRow(user)
    }
}

Notice:

No Adapter.

No ViewHolder.

No XML.


RecyclerView vs LazyColumn

RecyclerViewLazyColumn
Adapteritems {}
ViewHolderComposable
XML LayoutKotlin UI
LayoutManagerBuilt-in lazy layout
DiffUtilState-driven updates (or keyed items for stability)
View RecyclingComposition reuse and lazy item management

Compose doesn’t expose a ViewHolder, but it still avoids rendering everything at once.


Part 11 – Common Bugs

Bug 1

Store position.

Click



Save Position



Scroll



Wrong Item

Positions change.

Use the current binding position when needed, and prefer stable item IDs over cached positions.


Bug 2

Changing data without notifying Adapter.

RecyclerView still displays old content.


Bug 3

Using notifyDataSetChanged() for every update.

Terrible performance.

Prefer DiffUtil/ListAdapter.


Bug 4

Heavy work inside binding.

Leads to janky scrolling.


Internal Flow

Suppose user scrolls.

Finger Scroll



RecyclerView



Need New Row



Recycle Pool



Reuse ViewHolder



Adapter.onBind()



Display

Notice:

No inflation.

No new objects.

Just reuse.

That’s the secret behind RecyclerView’s efficiency.


Real-World Example

Instagram feed.

Post 1



Post 2



Post 3

Scroll 100 posts.

Instagram does not create 100 View trees.

Instead:

15 Views



Recycled



Bound



Recycled



Bound

Those same view holders display hundreds or thousands of different posts over time.


Best Practices

  • Prefer ListAdapter with DiffUtil for most list UIs.
  • Keep onBindViewHolder() lightweight.
  • Use image loading libraries (like Coil, Glide, or Picasso) instead of manual bitmap handling.
  • Avoid nested RecyclerViews unless necessary.
  • Use stable IDs when appropriate.
  • Don’t cache adapter positions.
  • In Compose, provide stable keys for list items when items can be inserted, removed, or reordered.

Mental Model

Think of RecyclerView as a hotel.

Rooms (Views)



Guest Leaves



Room Cleaned



New Guest



Same Room

The room isn’t rebuilt.

Only the occupant changes.

That’s exactly what RecyclerView does.


Interview Questions

  1. Why was RecyclerView introduced?
  2. Explain the responsibilities of RecyclerView, Adapter, and ViewHolder.
  3. What is view recycling, and why is it important?
  4. What’s the difference between onCreateViewHolder() and onBindViewHolder()?
  5. Why is notifyDataSetChanged() inefficient?
  6. How does DiffUtil improve performance?
  7. What is the role of a LayoutManager?
  8. Why should onBindViewHolder() avoid expensive operations?
  9. Compare RecyclerView and LazyColumn.
  10. How does RecyclerView display thousands of items while creating only a small number of Views?

Module 9 Summary

At this point, you’ve learned:

  • Why RecyclerView exists and how it solves large-list rendering.
  • The roles of RecyclerView, Adapter, ViewHolder, and LayoutManager.
  • How view recycling dramatically reduces memory usage and allocations.
  • Why DiffUtil and ListAdapter are preferred over blanket refreshes.
  • How Compose’s LazyColumn achieves the same high-level goal with a declarative API.

Next Module: Android Architecture (MVVM, Repository, Clean Architecture & SOLID)

This is arguably the most important module in the entire course.

So far, you’ve learned how Android works.

In Module 10, you’ll learn how professional Android applications are designed.

We’ll cover, in depth:

  • Why MVC failed on Android.
  • MVP vs MVVM vs MVI.
  • Why Google recommends MVVM.
  • ViewModel internals and lifecycle.
  • Repository pattern.
  • Use Cases (Domain layer).
  • Clean Architecture.
  • Dependency Inversion.
  • SOLID principles applied to Android.
  • Hilt and Dependency Injection (conceptually first).
  • Building a scalable, testable architecture.

This module is where individual Android concepts come together into a maintainable application structure—the same kind you’ll encounter in production codebases and technical interviews.