How to Create RecyclerView with CardView in Android (Step-by-Step)

How to Create RecyclerView with CardView in Android (Step-by-Step)


If you’re learning Android and want to build a clean, modern list (like a contacts screen or a product catalog), RecyclerView with CardView is a rock-solid approach. In this step-by-step guide, you’ll learn how to create RecyclerView with CardView in Android, wire it up in Kotlin (with a Java version too), handle item clicks, and follow current best practices. We’ll use MaterialCardView for a polished Material design look, ListAdapter + DiffUtil for efficient updates, and simple layout managers for linear lists and grids. By the end, you’ll have a reusable template you can drop into any beginner app.

What are RecyclerView and CardView?

Final UI of how to create RecyclerView with CardView in Android on a modern Material-styled phone list
Preview of a RecyclerView list built with CardView items in Material Design.

RecyclerView is the modern, flexible container for displaying large lists or grids of items. It reuses (recycles) item views as you scroll, making it more efficient than older approaches. You plug in a LayoutManager (linear, grid, or staggered), and an Adapter with a ViewHolder to bind your data.

CardView (and the newer MaterialCardView) gives each list item a card UI with rounded corners, elevation (shadow), and ripple feedback. For Material-themed apps, prefer com.google.android.material.card.MaterialCardView. Use AndroidX CardView only if you’re not using the Material Components library.

When should you use this pattern? Any time you need a scrollable list or grid of items in a classic View-based UI: chats, feeds, catalogs, settings lists, etc. This tutorial focuses on how to create RecyclerView with CardView in Android using XML layouts, which is perfect for students and beginners working in Android Studio.

What we’ll build

Visual flow of how to create RecyclerView with CardView in Android: data source, Adapter, LayoutManager, ViewHolder
How data and layout move through RecyclerView, Adapter, LayoutManager, and ViewHolder.

We’ll build a simple “People” list using RecyclerView and MaterialCardView. The list will show a name and subtitle for each person, support item clicks, and demonstrate both linear and grid layouts. You’ll see:

  • RecyclerView and CardView tutorial for beginners
  • Android RecyclerView step by step guide
  • CardView layout for RecyclerView item
  • RecyclerView adapter example in Kotlin and Java
  • Android list using RecyclerView and CardView

Prerequisites

  • Android Studio (latest stable)
  • Basic Kotlin or Java knowledge
  • A View-based Android project (Empty Activity template is fine)

Quick build flow: RecyclerView + MaterialCardView
1
Dependencies
Add RecyclerView + Material Components, enable viewBinding in Gradle.

2
Layouts
Create activity_main.xml with RecyclerView and item_user.xml with MaterialCardView.

3
Model
Data class (User) with id, name, subtitle.

4
Adapter
ListAdapter + DiffUtil + ViewHolder using View Binding; set click callback on the card.

5
Wire up
Set LayoutManager (Linear or Grid), attach adapter, submitList(data).

6
Polish
Optional ItemDecoration, setHasFixedSize when safe, test clicks and scrolling.

1) Project setup: dependencies and View Binding

Add libraries in app/build.gradle

Open your app module’s build.gradle and add the RecyclerView and Material Components dependencies. Also enable View Binding for safer, faster view access in ViewHolders.

Gradle
Code
android {
    class="cd-package">namespace "com.example.yourapp"
    compileSdk 34

    defaultConfig {
        applicationId "com.example.yourapp"
        minSdk 21
        targetSdk 34
        versionCode 1
        versionName "1.0"
    }

    buildFeatures {
        viewBinding true
    }
}

dependencies {
    implementation "androidx.recyclerview:recyclerview:1.4.0" // use latest
    implementation "com.google.android.material:material:1.12.0" // use latest
}

Sync your project.

2) Create your layouts (XML)

activity_main.xml: host the RecyclerView

Put a RecyclerView in your main layout. If you use View Binding (recommended), give it an ID like recyclerView.

XML
Code
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:clipToPadding="false"
        android:padding="16dp"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

item_user.xml: CardView layout for each RecyclerView item

Use MaterialCardView for a Material design card. Keep the item layout simple to ensure smooth scrolling.

XML
Code
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cardUser"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:clickable="true"
    android:focusable="true"
    app:cardUseCompatPadding="true"
    app:cardForegroundColor="?attr/colorSurface"
    app:strokeColor="?attr/colorOutline"
    app:strokeWidth="1dp"
    app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Material3.Corner.Medium"
    android:layout_marginBottom="8dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tvName"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Name"
            android:textAppearance="?attr/textAppearanceTitleMedium"
            android:textColor="?attr/colorOnSurface"/>

        <TextView
            android:id="@+id/tvSubtitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Subtitle"
            android:textAppearance="?attr/textAppearanceBodyMedium"
            android:textColor="?attr/colorOnSurfaceVariant"/>

    </LinearLayout>

</com.google.android.material.card.MaterialCardView>

3) Create a simple model

Kotlin data model

Kotlin
Code
data class User(
    val id: Long,
    val name: String,
    val subtitle: String
)

Java model (if using Java)

Java
Code
class="cd-keyword cd-access">public class UserJava {
    class="cd-keyword cd-access">private final long id;
    class="cd-keyword cd-access">private final String name;
    class="cd-keyword cd-access">private final String subtitle;

    class="cd-keyword cd-access">public UserJava(long id, String name, String subtitle) {
        this.id = id;
        this.name = name;
        this.subtitle = subtitle;
    }
    class="cd-keyword cd-access">public long getId() { return id; }
    class="cd-keyword cd-access">public String getName() { return name; }
    class="cd-keyword cd-access">public String getSubtitle() { return subtitle; }
}

4) Create RecyclerView Adapter and ViewHolder (Kotlin)

We’ll use ListAdapter with DiffUtil.ItemCallback (recommended) so list updates are efficient without calling notifyDataSetChanged(). We’ll also use View Binding in the ViewHolder.

Kotlin
Code
class UserAdapter(
    class="cd-keyword cd-access">private val onItemClick: (User) -> Unit
) : ListAdapter<User, UserAdapter.UserVH>(DIFF) {

    companion object {
        class="cd-keyword cd-access">private val DIFF = object : DiffUtil.ItemCallback<User>() {
            override fun areItemsTheSame(oldItem: User, newItem: User): Boolean =
                oldItem.id == newItem.id

            override fun areContentsTheSame(oldItem: User, newItem: User): Boolean =
                oldItem == newItem
        }
    }

    inner class UserVH(class="cd-keyword cd-access">private val binding: ItemUserBinding) :
        RecyclerView.ViewHolder(binding.root) {

        fun bind(item: User) {
            binding.tvName.text = item.name
            binding.tvSubtitle.text = item.subtitle
            binding.root.setOnClickListener { onItemClick(item) }
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserVH {
        val inflater = LayoutInflater.from(parent.context)
        val binding = ItemUserBinding.inflate(inflater, parent, false)
        return UserVH(binding)
    }

    override fun onBindViewHolder(holder: UserVH, position: Int) {
        holder.bind(getItem(position))
    }
}

5) Wire up RecyclerView in Activity/Fragment (Kotlin)

Set a LayoutManager, attach the adapter, and submit a list. Use LinearLayoutManager for a vertical list or GridLayoutManager for a grid.

Kotlin
Code
class MainActivity : AppCompatActivity() {

    class="cd-keyword cd-access">private lateinit var binding: ActivityMainBinding
    class="cd-keyword cd-access">private lateinit var adapter: UserAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        adapter = UserAdapter { user ->
            Toast.makeText(this, "Clicked: ${user.name}", Toast.LENGTH_SHORT).show()
        }

        // Choose your LayoutManager
        binding.recyclerView.layoutManager = LinearLayoutManager(this)
        // For grids:
        // binding.recyclerView.layoutManager = GridLayoutManager(this, 2)

        binding.recyclerView.adapter = adapter

        // Use this only if item size does not change with content and layout size stays constant:
        // binding.recyclerView.setHasFixedSize(true)

        // Optional: add a simple divider between items
        binding.recyclerView.addItemDecoration(
            DividerItemDecoration(this, RecyclerView.VERTICAL)
        )

        // Provide data
        val users = listOf(
            User(1, "Alice Johnson", "Android learner"),
            User(2, "Ben Carter", "Kotlin beginner"),
            User(3, "Chloe Lee", "UI enthusiast"),
            User(4, "Diego Perez", "Java starter"),
            User(5, "Eva Zhang", "App development learner")
        )
        adapter.submitList(users)
    }
}

6) RecyclerView adapter example in Java

If you’re learning Java, here’s a minimal adapter using ListAdapter, View Binding, and a click callback.

Java
Code
class="cd-keyword cd-access">public class UserAdapterJava extends ListAdapter<UserJava, UserAdapterJava.UserVH> {

    class="cd-keyword cd-access">public interface OnItemClick {
        void onClick(UserJava user);
    }

    class="cd-keyword cd-access">private final OnItemClick onItemClick;

    class="cd-keyword cd-access">public UserAdapterJava(class="cd-annotation">@NonNull OnItemClick onItemClick) {
        super(DIFF_CALLBACK);
        this.onItemClick = onItemClick;
    }

    class="cd-keyword cd-access">private static final DiffUtil.ItemCallback<UserJava> DIFF_CALLBACK =
            new DiffUtil.ItemCallback<UserJava>() {
                class="cd-annotation">@Override
                class="cd-keyword cd-access">public boolean areItemsTheSame(class="cd-annotation">@NonNull UserJava oldItem, class="cd-annotation">@NonNull UserJava newItem) {
                    return oldItem.getId() == newItem.getId();
                }
                class="cd-annotation">@Override
                class="cd-keyword cd-access">public boolean areContentsTheSame(class="cd-annotation">@NonNull UserJava oldItem, class="cd-annotation">@NonNull UserJava newItem) {
                    return oldItem.getName().equals(newItem.getName())
                            && oldItem.getSubtitle().equals(newItem.getSubtitle());
                }
            };

    class="cd-annotation">@NonNull
    class="cd-annotation">@Override
    class="cd-keyword cd-access">public UserVH onCreateViewHolder(class="cd-annotation">@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        ItemUserBinding binding = ItemUserBinding.inflate(inflater, parent, false);
        return new UserVH(binding);
    }

    class="cd-annotation">@Override
    class="cd-keyword cd-access">public void onBindViewHolder(class="cd-annotation">@NonNull UserVH holder, int position) {
        holder.bind(getItem(position), onItemClick);
    }

    static class UserVH extends RecyclerView.ViewHolder {
        class="cd-keyword cd-access">private final ItemUserBinding binding;
        UserVH(ItemUserBinding binding) {
            super(binding.getRoot());
            this.binding = binding;
        }
        void bind(UserJava user, OnItemClick onItemClick) {
            binding.tvName.setText(user.getName());
            binding.tvSubtitle.setText(user.getSubtitle());
            binding.getRoot().setOnClickListener(v -> onItemClick.onClick(user));
        }
    }
}

Set up the Java Activity

Java
Code
class="cd-keyword cd-access">public class MainActivityJava extends AppCompatActivity {

    class="cd-keyword cd-access">private ActivityMainBinding binding;
    class="cd-keyword cd-access">private UserAdapterJava adapter;

    class="cd-annotation">@Override
    class="cd-keyword cd-access">protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        adapter = new UserAdapterJava(user ->
                Toast.makeText(this, "Clicked: " + user.getName(), Toast.LENGTH_SHORT).show());

        binding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
        binding.recyclerView.setAdapter(adapter);

        List<UserJava> data = Arrays.asList(
                new UserJava(1, "Alice Johnson", "Android learner"),
                new UserJava(2, "Ben Carter", "Kotlin beginner"),
                new UserJava(3, "Chloe Lee", "UI enthusiast")
        );
        adapter.submitList(data);
    }
}

7) Handle item clicks in RecyclerView CardView

We passed a lambda (Kotlin) or interface callback (Java) into the adapter and set it on binding.root (the MaterialCardView). This is the simplest way to handle item clicks in a RecyclerView list. You could also attach listeners to child views (like a button inside the card) if needed.

8) Choose a LayoutManager: LinearLayoutManager vs GridLayoutManager

  • LinearLayoutManager (vertical or horizontal): Best for standard lists like messages or settings.
  • GridLayoutManager: Best for image galleries, product grids, dashboards. Choose a column count based on screen size.

LayoutManager When to use Pros Considerations Setup
LinearLayoutManager Messages, settings, any simple vertical list Fast to set up; predictable scrolling; great for text lists Not ideal for tile-based UIs; single column/row only recyclerView.layoutManager = LinearLayoutManager(this)
GridLayoutManager Image/product grids, dashboards, cards in multiple columns Multiple columns; works nicely with CardView tiles Pick column count that fits padding/card width recyclerView.layoutManager = GridLayoutManager(this, 2)
StaggeredGridLayoutManager (optional) Pinterest-style, cards with varying heights Masonry look without complex custom views More complex to fine-tune; beware item jumps with dynamic content recyclerView.layoutManager =
StaggeredGridLayoutManager(2, VERTICAL)

Switching is one line of code. For example, a two-column grid:

Kotlin
binding.recyclerView.layoutManager = GridLayoutManager(this, 2)

9) Performance and polish tips

  • Prefer MaterialCardView over the older CardView if you’re using Material Components. It supports shape theming, strokes, and state changes.
  • Use ListAdapter + DiffUtil for updates. Call submitList() instead of notifyDataSetChanged().
  • setHasFixedSize(true) only if adding/removing items or content updates won’t change the RecyclerView’s overall size.
  • Add spacing/dividers via ItemDecoration (as shown) so your item XML stays clean.
  • Keep item layouts lightweight and avoid deep nesting for smooth scrolling.
  • View Binding makes ViewHolder code safe and fast; prefer it over findViewById.

10) Going further: dynamic updates, huge lists, and sections

Updating the list dynamically

Because we used ListAdapter, you can submit a new list and DiffUtil will compute changes off the main thread:

Kotlin
Code
// Example: add a new user and update the list
val newList = adapter.currentList.toMutableList().apply {
    add(User(6, "Frank Moore", "New to RecyclerView"))
}
adapter.submitList(newList)

Large or paged data (Paging 3)

For very large lists or data that loads in pages, use the Paging 3 library with RecyclerView’s PagingDataAdapter:

Kotlin
Code
class PagedUserAdapter(
    class="cd-keyword cd-access">private val onItemClick: (User) -> Unit
) : PagingDataAdapter<User, PagedUserAdapter.UserVH>(DIFF) {
    // Similar to ListAdapter, but submit PagingData from a Pager
}

Hook it up with a Pager in your ViewModel and collect PagingData<User> from a Flow to submit to the adapter.

Headers, footers, and sections

If you need multiple adapters (e.g., a header + list + footer), consider ConcatAdapter instead of nested RecyclerViews for better performance and simpler coordination.

Output / Result

After running the app, you’ll see a vertically scrolling list of cards. Each card shows a name and subtitle with Material styling (rounded corners and ripple). Tapping a card triggers a click callback (we showed a Toast). Switching to GridLayoutManager displays the same cards in a neat grid without changing the Adapter.

Run-ready checklist: RecyclerView + CardView
  • Gradle has androidx.recyclerview:recyclerview and com.google.android.material:material.
  • viewBinding is enabled in build.gradle.
  • activity_main.xml contains a RecyclerView with a valid @+id/recyclerView.
  • item_user.xml root is MaterialCardView with android:clickable="true" and android:focusable="true" for ripple and accessibility.
  • Adapter extends ListAdapter<User, VH> and uses a correct DiffUtil.ItemCallback.
  • Click handling is set on binding.root (the card) or needed child views.
  • A LayoutManager is set exactly once (e.g., LinearLayoutManager or GridLayoutManager).
  • Attach the adapter to the RecyclerView and call submitList(yourData).
  • Optional: add ItemDecoration for spacing; use setHasFixedSize(true) only when safe.
  • Run the app and verify smooth scroll, correct card ripple, and item clicks firing.

Common mistakes to avoid

  • Putting heavy work (like image decoding) on the main thread inside onBindViewHolder. Use libraries like Coil/Glide for images.
  • Calling notifyDataSetChanged() for every change. Prefer ListAdapter with DiffUtil or Paging 3.
  • Using margins inside each item to space rows. Prefer a RecyclerView ItemDecoration to centralize spacing.
  • Always calling setHasFixedSize(true). Only use it when item content and list size don’t affect the RecyclerView’s size.

FAQ

What is RecyclerView and why use it over ListView?

RecyclerView is a flexible, efficient list and grid container that supports view recycling, animations, multiple layout managers, and powerful adapters. While ListView isn’t deprecated, RecyclerView is the modern, more capable choice for new View-based UIs.

How do I add a CardView inside each RecyclerView item?

Create an item XML with MaterialCardView as the root (as shown in item_user.xml). Inflate that layout in your Adapter’s ViewHolder and bind the data to its child views. Using MaterialCardView gives you shape theming, strokes, and ripple out of the box.

How do I create a RecyclerView adapter in Kotlin for beginners?

Extend ListAdapter<T, VH>, implement a DiffUtil.ItemCallback<T>, create a ViewHolder class that holds a binding to your item layout, and override onCreateViewHolder and onBindViewHolder. Use submitList() to display or update items.

How can I handle item clicks in a RecyclerView list?

Pass a click lambda or interface into your adapter’s constructor and set it on binding.root (the card) or on specific child views. This keeps click logic separate from view binding and avoids memory leaks.

Which LayoutManager should I use for RecyclerView: LinearLayoutManager or GridLayoutManager?

Use LinearLayoutManager for standard lists (vertical or horizontal). Use GridLayoutManager for grid layouts like image galleries or product tiles. You can switch with one line of code.

Compose note

Jetpack Compose is recommended for new UI development (use LazyColumn + Card). However, RecyclerView remains fully supported and is excellent for existing or hybrid apps. You can also embed Compose content inside RecyclerView items via ComposeView if you’re migrating gradually.

Recap: how to set up RecyclerView with CardView in Android Studio

  • Add RecyclerView and Material Components dependencies; enable View Binding.
  • Create RecyclerView in your Activity layout and a MaterialCardView-based item layout.
  • Build a ListAdapter with DiffUtil and a ViewHolder that binds your data.
  • Set a LayoutManager (Linear or Grid) and submitList() to display items.
  • Handle card clicks via a callback. Add dividers or spacing with ItemDecoration.

Sources / Further reading

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted