Mastering RecyclerView: How to Handle Multiple View Types in Android

Introduction

In Android development, RecyclerView is a powerful component for efficiently displaying large lists or grids. However, many real-world applications require lists that contain different layout types. For example, a list might include text items, images, advertisements, or other UI components.

This is where multiple view types in RecyclerView become useful.

Instead of creating separate RecyclerViews for different content types, developers can use a single adapter that supports multiple layouts. This approach keeps the UI efficient, scalable, and easier to maintain.

In this article, we will explore how to implement multiple view types in a RecyclerView using Kotlin, including clean architecture and best practices, and provide working code examples.

What Are Multiple View Types?

In many applications, list items are not always identical. Different data structures may require different UI layouts.

For example:

  • A news feed may contain articles, advertisements, and videos.

  • A chat application may display sent and received messages differently.

  • A product list might include banners, categories, and product cards.

Instead of creating multiple RecyclerViews, developers can define different view types inside a single RecyclerView adapter. The adapter then decides which layout to display for each item.

This method ensures better performance and simpler list management.

Step-by-Step Implementation

Step 1: Create Your Data Models

First, define multiple item types. Kotlin’s sealed classes are ideal for representing different list item structures.

sealed class ListItem {
    data class TextItem(val text: String) : ListItem()
    data class ImageItem(val imageUrl: String) : ListItem()
}

In this example:

  • TextItem represents a list item containing text.

  • ImageItem represents a list item containing an image.

Using sealed classes allows the adapter to safely determine which type of item it is working with.

Step 2: Create Layout Files

Each view type needs its own layout file.

item_text.xml

<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/textView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp"
    android:textSize="18sp"
    android:textColor="@android:color/black" />

item_image.xml

<ImageView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/imageView"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:scaleType="centerCrop" />

Each layout represents a specific item type that will be displayed in the RecyclerView.

Step 3: Create the Adapter

The adapter determines which layout should be used for each item.

class MultiViewAdapter(private val items: List<ListItem>) :
    RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    companion object {
        private const val TYPE_TEXT = 0
        private const val TYPE_IMAGE = 1
    }

    override fun getItemViewType(position: Int): Int {
        return when (items[position]) {
            is ListItem.TextItem -> TYPE_TEXT
            is ListItem.ImageItem -> TYPE_IMAGE
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        return when (viewType) {
            TYPE_TEXT -> {
                val view = inflater.inflate(R.layout.item_text, parent, false)
                TextViewHolder(view)
            }
            TYPE_IMAGE -> {
                val view = inflater.inflate(R.layout.item_image, parent, false)
                ImageViewHolder(view)
            }
            else -> throw IllegalArgumentException("Invalid view type")
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (val item = items[position]) {
            is ListItem.TextItem -> (holder as TextViewHolder).bind(item)
            is ListItem.ImageItem -> (holder as ImageViewHolder).bind(item)
        }
    }

    override fun getItemCount(): Int = items.size

    class TextViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private val textView: TextView = itemView.findViewById(R.id.textView)

        fun bind(item: ListItem.TextItem) {
            textView.text = item.text
        }
    }

    class ImageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private val imageView: ImageView = itemView.findViewById(R.id.imageView)

        fun bind(item: ListItem.ImageItem) {
            Glide.with(itemView.context).load(item.imageUrl).into(imageView)
        }
    }
}

Key responsibilities of the adapter:

  • getItemViewType() determines which layout type to use.

  • onCreateViewHolder() inflates the correct layout.

  • onBindViewHolder() binds the data to the correct ViewHolder.

Step 4: Set Up in Activity or Fragment

Now configure the RecyclerView in your Activity or Fragment.

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val recyclerView: RecyclerView = findViewById(R.id.recyclerView)
        recyclerView.layoutManager = LinearLayoutManager(this)

        val items = listOf(
            ListItem.TextItem("Hello RecyclerView!"),
            ListItem.ImageItem("https://picsum.photos/300"),
            ListItem.TextItem("Multiple View Types Example"),
            ListItem.ImageItem("https://picsum.photos/400")
        )

        recyclerView.adapter = MultiViewAdapter(items)
    }
}

The RecyclerView will automatically display different layouts depending on the item type.

Output

After implementing the adapter, the RecyclerView will display a smooth list containing both text and image items. Each item uses its corresponding layout while remaining part of a single RecyclerView.

This ensures efficient memory usage and smooth scrolling.

Best Practices

When working with multiple view types in RecyclerView, developers should follow these best practices:

  • Use sealed classes or enums to represent item types clearly.

  • Keep the adapter stateless and manage data externally using components like ViewModel.

  • Use DiffUtil for large datasets to improve performance and reduce unnecessary UI updates.

  • Reuse ViewHolders whenever possible to avoid repeated layout inflation.

  • Keep view-binding logic inside ViewHolders for better separation of concerns.

Following these practices helps maintain a clean and scalable architecture.

Conclusion

Handling multiple view types in RecyclerView is essential for building dynamic and modern Android interfaces. Many real-world applications require lists that contain different types of content such as text, images, advertisements, or banners.

By defining multiple view types within a single adapter, developers can efficiently manage complex lists while maintaining good performance and clean code structure.

This approach is widely used in applications such as chat platforms, dashboards, social feeds, and e-commerce apps because it keeps the UI flexible, scalable, and easy to maintain.