RecyclerView
๐ RecyclerView
๐งฉ The Adapter Pattern
RecyclerView itself knows nothing about your data โ an Adapter is the bridge between a data source (a List<T>, most often) and the actual item Views on screen, implemented with three required overrides:
onCreateViewHolder โ Called Rarely
Inflates a brand-new item layout and wraps it in a ViewHolder. Only called enough times to fill the visible screen plus a small buffer โ not once per data item, which is the entire performance idea behind RecyclerView.
onBindViewHolder โ Called Constantly
Takes an already-created ViewHolder and fills it with data for a specific position. This runs every time a recycled View scrolls back on screen with new data โ it should stay cheap, since it fires very frequently during scrolling.
๐๏ธ ViewHolder โ Caching View Lookups
ViewHolder exists purely to hold references to one item's Views (via view binding, per Chapter 3) so they're looked up once, at creation, rather than re-searched on every bind. Without it, every scroll frame would repeat the equivalent of a fresh findViewById for every visible item, which used to be a genuine performance problem in Android's early list-view APIs.
๐ Wiring It Up in an Activity
A layoutManager is a separate, required piece โ it decides how items are arranged (LinearLayoutManager for a simple vertical or horizontal list, GridLayoutManager for a grid). RecyclerView deliberately splits "how data becomes Views" (the Adapter) from "how those Views are positioned" (the LayoutManager) โ two independent, swappable concerns.
โก DiffUtil โ Efficient List Updates
Calling notifyDataSetChanged() after a data change tells the Adapter "something changed, redraw everything" โ correct, but wasteful, and it kills any scroll-position-preserving item animations. DiffUtil instead computes the minimal set of actual changes between an old list and a new one:
areItemsTheSame answers "is this the same real-world contact?" (usually by a stable ID) โ areContentsTheSame answers "has anything about it actually changed?" DiffUtil then tells the Adapter to animate exactly the items that were added, removed, moved, or changed, leaving everything else alone.
DiffUtil vs React's Key-Based Reconciliation
| React | RecyclerView + DiffUtil | |
|---|---|---|
| Identity check | key prop on each list item | areItemsTheSame() |
| Change check | Shallow prop comparison (or memo) | areContentsTheSame() |
| Result | Minimal virtual DOM diff applied | Minimal RecyclerView item animations applied |
In practice, most real code extends ListAdapter<Contact, ContactViewHolder> instead of plain RecyclerView.Adapter โ it wraps DiffUtil internally (via a smaller DiffUtil.ItemCallback) and exposes a single submitList(newList) call that handles the diffing and updating automatically. The manual DiffUtil.Callback above is worth understanding once, precisely so ListAdapter's automatic version doesn't feel like unexplained magic.
๐ Handling Clicks on List Items
Click handling is set up per-item inside onBindViewHolder, most cleanly by passing a lambda into the Adapter's constructor โ a direct callback-parameter pattern, same shape as passing a lambda to map/filter back in Kotlin Fundamentals Chapter 6:
๐ป Coding Challenges
Challenge 1: A Basic RecyclerView List
Build a RecyclerView showing a list of at least 5 data class Product(val name: String, val price: Double) items, with an item layout showing both fields. Write the Adapter and ViewHolder, and wire it up in the Activity with a LinearLayoutManager.
Goal: Practice the full Adapter/ViewHolder/getItemCount setup from scratch.
Challenge 2: Click Handling
Extend Challenge 1's Adapter to accept an onProductClick: (Product) -> Unit lambda in its constructor, set on each item's root view in onBindViewHolder. In the Activity, log the clicked product's name via the passed-in lambda.
Goal: Practice the constructor-lambda click-handling pattern.
Challenge 3: A DiffUtil.Callback
Write a ProductDiffCallback implementing DiffUtil.Callback for two List<Product>, using the product name as the stable identity for areItemsTheSame, and full data class equality for areContentsTheSame. In a comment, describe what would happen (in terms of item animations) if two lists differed by one product's price changing.
Goal: Practice writing DiffUtil.Callback correctly, understanding the distinct roles of its two comparison methods.
Every long list in a real Android app โ a chat history, an e-commerce catalog, a social feed โ uses this exact same adapter/ViewHolder shape underneath, no matter how visually different the items look. Once this pattern is genuinely comfortable, most list-related work becomes "design this one item's layout," not "figure out list infrastructure again."
๐ฏ What's Next
Next chapter: Fragments โ fragment lifecycle, the fragment manager, and communicating with the hosting Activity.