Introduction
RecyclerView with GridLayoutManager is the recommended replacement for legacy GridView in modern Android apps. It offers better performance, flexible layouts, and clearer adapter patterns. A solid setup includes predictable item sizing, spacing, click handling, and efficient binding behavior.
Basic Grid Layout Setup
Define the RecyclerView in XML.
1<?xml version="1.0" encoding="utf-8"?>
2<androidx.recyclerview.widget.RecyclerView
3 xmlns:android="http://schemas.android.com/apk/res/android"
4 android:id="@+id/recyclerView"
5 android:layout_width="match_parent"
6 android:layout_height="match_parent"
7 android:padding="8dp"
8 android:clipToPadding="false" />
Initialize with GridLayoutManager in activity or fragment.
1import android.os.Bundle
2import androidx.appcompat.app.AppCompatActivity
3import androidx.recyclerview.widget.GridLayoutManager
4import androidx.recyclerview.widget.RecyclerView
5
6class MainActivity : AppCompatActivity() {
7 override fun onCreate(savedInstanceState: Bundle?) {
8 super.onCreate(savedInstanceState)
9 setContentView(R.layout.activity_main)
10
11 val recycler = findViewById<RecyclerView>(R.id.recyclerView)
12 recycler.layoutManager = GridLayoutManager(this, 3)
13 recycler.setHasFixedSize(true)
14 recycler.adapter = GridAdapter((1..30).map { "Item $it" })
15 }
16}
This gives a simple three-column grid similar to classic grid behavior.
Adapter and ViewHolder Implementation
Keep bind logic focused and avoid expensive work in onBindViewHolder.
1import android.view.LayoutInflater
2import android.view.View
3import android.view.ViewGroup
4import android.widget.TextView
5import androidx.recyclerview.widget.RecyclerView
6
7class GridAdapter(
8 private val items: List<String>,
9 private val onClick: (String) -> Unit = {}
10) : RecyclerView.Adapter<GridAdapter.VH>() {
11
12 class VH(view: View) : RecyclerView.ViewHolder(view) {
13 val title: TextView = view.findViewById(R.id.title)
14 }
15
16 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
17 val view = LayoutInflater.from(parent.context)
18 .inflate(R.layout.grid_item, parent, false)
19 return VH(view)
20 }
21
22 override fun onBindViewHolder(holder: VH, position: Int) {
23 val item = items[position]
24 holder.title.text = item
25 holder.itemView.setOnClickListener { onClick(item) }
26 }
27
28 override fun getItemCount(): Int = items.size
29}
A callback-based click API keeps adapter reusable.
Add Consistent Grid Spacing
GridLayoutManager does not add classic GridView spacing automatically. Use ItemDecoration.
1import android.graphics.Rect
2import android.view.View
3import androidx.recyclerview.widget.RecyclerView
4
5class GridSpacingDecoration(
6 private val spanCount: Int,
7 private val spacing: Int
8) : RecyclerView.ItemDecoration() {
9
10 override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
11 val position = parent.getChildAdapterPosition(view)
12 val column = position % spanCount
13
14 outRect.left = spacing - column * spacing / spanCount
15 outRect.right = (column + 1) * spacing / spanCount
16 if (position < spanCount) outRect.top = spacing
17 outRect.bottom = spacing
18 }
19}
Apply once:
recycler.addItemDecoration(GridSpacingDecoration(3, 16))
This improves visual consistency across screens.
Efficient Image Grids
If grid items include images, use an async image library instead of manual bitmap decode.
1// Example with Coil inside onBindViewHolder
2// imageView.load(url) {
3// crossfade(true)
4// placeholder(R.drawable.placeholder)
5// }
Avoid decoding bitmaps on main thread. Main-thread decode causes dropped frames and poor scroll performance.
Span Customization
You can mimic mixed-size tile layouts by customizing span size.
1val layoutManager = GridLayoutManager(this, 3)
2layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
3 override fun getSpanSize(position: Int): Int {
4 return if (position % 7 == 0) 3 else 1
5 }
6}
7recycler.layoutManager = layoutManager
This is a common pattern for featured-card sections.
Migration Tips from Old GridView
When migrating:
Move click logic into adapter callbacks.
Replace old adapter with view holder pattern.
Add explicit spacing decoration.
Validate accessibility and focus behavior.
Migration is usually easiest when one reusable grid component is created and shared across screens.
Common Pitfalls
Inflating item views incorrectly with wrong parent parameters.
Doing expensive transforms in onBindViewHolder.
Forgetting spacing decoration and getting cramped layouts.
Failing to handle data updates with proper adapter notifications.
Ignoring accessibility labels and touch targets in grid items.
Summary
Use RecyclerView plus GridLayoutManager as the modern GridView replacement.
Keep adapter binding lightweight and callback-driven.
Add explicit spacing and stable item sizing for consistent UI.
Use async image loading for media-heavy grids.
Build one reusable grid pattern and apply it consistently across app screens.