Search code examples
androidandroid-architecture-navigationdrawerlayoutandroid-navigation-graph

Android Navigation Component with Drawer Layout and Dynamic Menu Items


So, here is what I want.

The top level data is structured like below :

Section 1 Title -> Section 1 Content (List)
Section 2 Title -> Section 2 Content (List)
.
.
.
Section N Title -> Section N Content (List)

I want to display the section titles in the side menu which opens when the hamburger is icon is selected and when a specific section is selected, the side menu closes and the content for that section is loaded in the screen under the action bar.

So, the relevant UI components to be used are : Toolbar, DrawerLayout (for Side Menu) and Fragment for loading the content in the center.

Now, I am trying to use the Navigation Components and get as much benefits of it as possible. The thing that I am unable to get to work is :

  1. Loading dynamic items in the side menu. The examples show using the android menu resources. I would like to use my own recycler view and load the menu dynamically.
  2. How to define the navGraph for a data structure like this. I tried to create an action from the ContentFragment to itself but I wasn't sure this was right because, there is no action inside it that takes the UI from one ContentFragment to another ContentFragment. It is a top level action from the side menu that loads a different ContentFragment.

Apart from the above 2 questions, I want to know if this is even a right candidate for using navigation components or is it better to use the traditional approach?


Solution

  • Implementing this is easily done with AndroidNavigation components.

    1 - Dynamic DrawerLayout can be implemented by adding RecyclerView inside com.google.android.material.navigation.NavigationView

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="false">
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rvDrawer"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    
    </com.google.android.material.navigation.NavigationView>
    

    2 - Activity navigation init when using DrawerLayout

    appBarConfiguration = AppBarConfiguration(
        setOf(R.id.mainFragment),
        binding.drawerLayout
    )
    
    binding.navView.setupWithNavController(navController)
    
    // Setup action bar with nav controller
    setupActionBarWithNavController(navController, appBarConfiguration)
    

    3 - com.google.android.material.navigation.NavigationView init

    private fun loadDrawerItems(items: Array<String>) {
            binding.rvDrawer.apply {
                layoutManager = LinearLayoutManager(this@MainActivity)
                adapter = DrawerAdapter(items, this@MainActivity)
            }
        }
    

    4 - DrawerAdapter notify activity when the user clicks on the item

     override fun onDrawerItemClick(value: String) {
            binding.drawerLayout.closeDrawer(GravityCompat.START)
            val direction = NavGraphDirections.refreshMainFragment(value)
            navController.navigate(direction)
        }
    

    5 - Navigation

    <?xml version="1.0" encoding="utf-8"?>
    <navigation
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/nav_graph"
        app:startDestination="@id/mainFragment">
    
        <fragment
            android:id="@+id/mainFragment"
            android:name="dh.sos.MainFragment"
            android:label="Main fragment">
            <argument
                android:name="value"
                app:argType="string"
                android:defaultValue="Default value"/>
    
    
        </fragment>
    
        <action android:id="@+id/refreshMainFragment"
            app:destination="@id/mainFragment"
            app:popUpTo="@id/mainFragment"
            app:popUpToInclusive="true">
    
            <argument
                android:name="value"
                app:argType="string"
                android:defaultValue="Default value"/>
        </action>
    </navigation>
    

    app:popUpToInclusive="true" indicates that popUpTo destination should be also removed from the stack, which means every time when we call this action we'll get a new instance of Fragment.

    Full source code: https://github.com/dautovicharis/sos_android/tree/q_68441622