Navigation Drawer With Submenu In Android

4 min read

A navigation drawer with submenu in Android remains one of the most effective design patterns for organizing complex application hierarchies. While the standard navigation drawer handles flat lists of destinations effortlessly, modern apps often require nested categories—think settings grouped by function, product catalogs divided by department, or multi-module enterprise tools. In real terms, implementing expandable groups inside the drawer requires a solid understanding of the NavigationView, custom adapters, or the modern NavigationRail and ModalNavigationDrawer composables in Jetpack Compose. This guide walks through the architecture, XML implementation, and Compose equivalents needed to build a reliable, accessible, and maintainable expandable navigation drawer.

Understanding the Core Components

Before diving into code, it is crucial to identify the moving parts. Consider this: a standard drawer relies on a DrawerLayout as the root container, a NavigationView for the menu content, and a menu resource XML defining items. To support submenus, the data model must shift from a flat list to a tree structure: Parent Items (Headers/Groups) containing Child Items (Destinations) That's the whole idea..

In the traditional View system, the NavigationView does not natively support expandable lists with smooth animations out of the box. Also, developers typically choose between two approaches: using a RecyclerView inside the NavigationView header/layout with a custom ExpandableRecyclerAdapter, or leveraging a third-party library like MaterialDrawer or AdvancedNavigationDrawer. For a dependency-free, lightweight solution, a custom RecyclerView implementation offers the most control over UI states, click handling, and animation logic Simple as that..

Building the Data Model

A clean data model separates the UI logic from the data structure. Create a sealed class or interface hierarchy to represent the two distinct view types: the Group (expandable header) and the Item (navigation destination).

sealed interface DrawerItem {
    data class Group(
        val title: String,
        @DrawableRes val iconRes: Int,
        val children: List,
        var isExpanded: Boolean = false
    ) : DrawerItem

    data class Child(
        val title: String,
        @DrawableRes val iconRes: Int,
        val destinationRoute: String // Or Menu Item ID
    ) : DrawerItem
}

This structure allows the adapter to differentiate view types via instanceof checks (or when expressions in Kotlin) and manage the expansion state directly inside the Group object. Storing isExpanded in the model simplifies state restoration during configuration changes, provided you use ViewModel or onSaveInstanceState That's the part that actually makes a difference. But it adds up..

Short version: it depends. Long version — keep reading.

Implementing the Custom Adapter

The heart of the submenu logic lives in a RecyclerView.Adapter capable of handling two view holders: GroupViewHolder and ChildViewHolder.

The View Holders

The GroupViewHolder binds the parent title, icon, and a rotation-capable expand/collapse indicator (typically a chevron icon). The ChildViewHolder binds the destination label and icon, usually indented visually to denote hierarchy.

class GroupViewHolder(view: View) : RecyclerView.ViewHolder(view) {
    val title: TextView = view.findViewById(R.id.tv_group_title)
    val icon: ImageView = view.findViewById(R.id.iv_group_icon)
    val arrow: ImageView = view.findViewById(R.id.iv_arrow_indicator)
    val container: View = view.findViewById(R.id.group_container)
}

class ChildViewHolder(view: View) : RecyclerView.id.In real terms, findViewById(R. Which means findViewById(R. Even so, id. Here's the thing — findViewById(R. And iv_child_icon)
    val container: View = view. But tv_child_title)
    val icon: ImageView = view. And viewHolder(view) {
    val title: TextView = view. id.

### The Adapter Logic

Override `getItemViewType` to return distinct integers for Groups and Children. In `onBindViewHolder`, the critical logic resides in the Group binding:

1.  **Rotate Arrow:** Animate the arrow icon rotation (0 to 180 degrees) based on `group.isExpanded`.
2.  **Toggle Visibility:** When the group container is clicked, toggle `group.isExpanded`.
3.  **Notify Range Changes:** Calculate the index range of children immediately following the group in the flattened list. Call `notifyItemRangeInserted` or `notifyItemRangeRemoved` for smooth default animations.

```kotlin
inner class DrawerAdapter(private val items: MutableList) : 
    RecyclerView.Adapter() {

    // ... ViewType constants and onCreateViewHolder ...

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val item = items[position]
        when (holder) {
            is GroupViewHolder -> bindGroup(holder, item as DrawerItem.Group, position)
            is ChildViewHolder -> bindChild(holder, item as DrawerItem.

    private fun bindGroup(holder: GroupViewHolder, group: DrawerItem.And group, position: Int) {
        holder. title.text = group.But title
        holder. icon.Here's the thing — setImageResource(group. iconRes)
        holder.arrow.In practice, rotation = if (group. isExpanded) 180f else 0f
        
        holder.container.setOnClickListener {
            group.That said, isExpanded = ! group.Worth adding: isExpanded
            val startIndex = position + 1
            val childCount = group. children.Worth adding: size
            
            if (group. isExpanded) {
                items.addAll(startIndex, group.That said, children)
                notifyItemRangeInserted(startIndex, childCount)
            } else {
                val endIndex = startIndex + childCount
                val removedItems = items. On the flip side, subList(startIndex, endIndex)
                items. Now, removeAll(removedItems)
                notifyItemRangeRemoved(startIndex, childCount)
            }
        }
    }
    
    private fun bindChild(holder: ChildViewHolder, child: DrawerItem. Now, child) {
        holder. title.text = child.Here's the thing — title
        holder. icon.setImageResource(child.Consider this: iconRes)
        holder. container.setOnClickListener {
            // Handle Navigation: Close drawer, work through to child.destinationRoute
            onItemClick(child.

**Pro Tip:** To prevent visual glitches during rapid clicks, disable the click listener briefly during animation or use `DiffUtil` with `ListAdapter` for a more dependable, modern approach.

## Integrating with DrawerLayout and NavigationView

Place the `RecyclerView` inside the `NavigationView` layout file (`nav_view.xml`). Remove the standard `app:menu` attribute from the `NavigationView` XML, as the RecyclerView replaces the native menu inflation.

```xml


    
        
Hot and New

Published Recently

You Might Find Useful

People Also Read

Thank you for reading about Navigation Drawer With Submenu In Android. We hope the information has been useful. Feel free to contact us if you have any questions. See you next time — don't forget to bookmark!
⌂ Back to Home