Android Navigation Architecture Component - Nav Drawer Icons

Advice-Dog picture Advice-Dog · Jul 26, 2018 · Viewed 9.1k times · Source

I'm currently using the Android Architecture Component's Navigation, but I'm running into an issue with my Navigation Drawer. It shows the hamburger menu when at my starting destination, but other Fragments are showing the up arrow. I believe I've setup my navigation_graph incorrectly.

Here you can see my nav drawer, showing 2 items, Home and Settings. When in either of these Fragments, you should see the Hamburger icon.

nav drawer

However, when navigating to the Settings Fragment, it shows the Up arrow.

toolbar

navigation.graph.xml

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    app:startDestination="@id/nav_home">

    <!-- Start at HomeFragment -->
    <fragment
        android:id="@+id/nav_home"
        android:name=".HomeFragment"
        android:label="@string/home">

        <!-- Navigate to the Search -->
        <action
            android:id="@+id/action_nav_home_to_nav_search"
            app:destination="@id/nav_search" />
    </fragment>


    <fragment
        android:id="@+id/nav_settings"
        android:name=".SettingsFragment"
        android:label="@string/settings">

        <!-- Navigate to the Search -->
        <action
            android:id="@+id/action_nav_settings_to_nav_search"
            app:destination="@id/nav_search" />
    </fragment>



    <fragment
        android:id="@+id/nav_search"
        android:name=".SearchFragment"
        android:label="@string/search" />

</navigation>

I feel like HomeFragment and SettingsFragment should be related somehow but I'm not sure how to define that.

main_drawer.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item
            android:id="@id/nav_home"
            android:icon="@drawable/ic_home_white_24dp"
            android:title="@string/home" />

        <item
            android:id="@id/nav_settings"
            android:icon="@drawable/ic_settings_white_24dp"
            android:title="@string/settings" />
    </group>
</menu>

MainActivity

And then within MainActivity, I just set it up like this. I called setupActionBarWithNavController, but I also have to actually setup the nav drawer myself, and handle the onNavigationItemSelected.

private fun setupNavigation() {
    navController = findNavController(R.id.mainNavigationFragment)
    setupActionBarWithNavController(this, navController, drawer_layout)

    val toggle = ActionBarDrawerToggle(
                this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
        drawer_layout.addDrawerListener(toggle)
        toggle.syncState()

    nav_view.setNavigationItemSelectedListener(this)
}

override fun onNavigationItemSelected(item: MenuItem): Boolean {
    val current = navController.currentDestination.id
    if (item.itemId != current) {
        navController.navigate(item.itemId)
    }

    drawer_layout.closeDrawers()
    return true
}

build.gradle

// Navigation
implementation 'android.arch.navigation:navigation-fragment-ktx:1.0.0-alpha04'
implementation 'android.arch.navigation:navigation-ui-ktx:1.0.0-alpha04'

Thanks.

Answer

Almighty picture Almighty · Nov 16, 2018

In newer alphas (I have 1.0.0-alpha07) they added possibility to define topLevelDestinationIds when calling AppBarConfiguration constructor.

So I setup my NavController like this

    val navController = findNavController(R.id.nav_host_fragment)
    val config = AppBarConfiguration(
        setOf(
            R.id.fistTopFragment,
            R.id.secondTopFragment,
            ...
        ),
        dr.drawerLayout
    )
    tb.setupWithNavController(navController, config)

Where dr is MaterialDrawer and tb of course Toolbar.

Then it behaves more like Gmail, at least for the ActionBarDrawerToggle, the back stack is still preserved. Since I must handle item selection in MaterialDrawer by myself, I'm going to reduce back stack actions using global navigation actions with inclusive popTo to the root fragment of the navigation graph and use something like a "Welcome screen" for now.

Another way around could be custom handling of the onBackPressed. You must remove app:defaultNavHost="true" from your host fragment in activity layout first. Something like this

override fun onBackPressed() {
    val navController = findNavController(R.id.nav_host_fragment)
    if (navController.currentDestination == null
        || navController.currentDestination!!.id in setOf(
            R.id.fistTopFragment,
            R.id.secondTopFragment,
            ...
        )
    ) {
        super.onBackPressed()
    } else {
        navController.navigateUp()
    }
}

Sorry about the code, I'm still learning Kotlin, so there is probably much nicer way of doing this.