Android Navigation Architecture Component - Nav Drawer Icons
E

7

17

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.

Extremist answered 25/7, 2018 at 23:8 Comment(6)
Just to confirm, you want Hamburger icon with Settings page also or some other icon? But not up button in Setting page.Herr
Yes. Both would show the hamburger icon. As they're both items within the nav drawer. However, going to another screen, such as "Search" or "Details" would show an up icon.Extremist
Use two activities for Home and Settings page. Create a base activity and extend both the activities from it. In base activity call initialize navigation drawer and its functionalities. This should solve your issue.Herr
I'm using the navigation library to reduce the number of Activities. I'm trying to following Google's suggestion of One Activity, Many Fragments.Extremist
Other solution can be use FrameLayout as container in common activity for both Home and Settings page. and replace the fragments programmatically.Herr
Let us continue this discussion in chat.Herr
T
10

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.

Tolyl answered 16/11, 2018 at 18:51 Comment(0)
A
4

I am afraid it is a feature of navigation component.

The action bar will also display the Up button when you are on a non-root destination and the drawer icon when on the root destination, automatically animating between them.

If you want, you can try to use setupWithNavController(NavigationView, NavController) and handle the toolbar yourself.

Antimagnetic answered 31/7, 2018 at 9:11 Comment(5)
I'm trying to follow the guidelines, and show a hamburger icon on "root destinations", and the up button on "non-root destinations", but it shows the up button on other "root destinations" other than the "home destination".Extremist
Well, according to creators it is intended behavior, so you are jus t fighting the library. issuetracker.google.com/issues/80555718 As for me, I actually like to have one starting screen even for drawer. When I go to settings, it is very likely, that when I press back, I want to go back to using the app and not end up in launcher.Antimagnetic
Actually, one of the comments say it's actually a bug. And it's being tracked. issuetracker.google.com/issues/109675998Extremist
The issue you are referring to talks about nested graphs. That does not seem to be your case. But by all means give the new version a try.Antimagnetic
I realized that I'm actually running alpha04, whereas the fix was in alpha02. I tried making my root destinations a nested graph, which makes a lot more sense, but it's still the same. And it's definitely not right, as going to a Fragment that SHOULD show the UP icon, pressing that doesn't work as expected, it opens the nav drawer instead of moving back.Extremist
I
2

I made simple example for this issue. Solution is almost same as Almighty's answer. https://github.com/isaul32/android-sunflower

Create set of top level destinations at first

val topLevelDestinations = setOf(R.id.garden_fragment,
        R.id.plant_list_fragment)
appBarConfiguration = AppBarConfiguration.Builder(topLevelDestinations)
        .setDrawerLayout(drawerLayout)
        .build()

and then override onSupportNavigateUp function like this

override fun onSupportNavigateUp(): Boolean {
    return NavigationUI.navigateUp(navController, appBarConfiguration)
}
Iinden answered 9/12, 2018 at 18:53 Comment(0)
D
1

Back arrow appearing on tab fragments associated with BottomNavigationView is an intended behaviour. However haven't seen it being used "as is" even in famous apps (Instagram, Youtube which have bottom tabs). If you navigate to different bottom tabs in Youtube app for example and click device back option, you'll notice it goes to previous tab fragment and not exit app. So bottomnavigationview tab fragments are not root destinations here.

Want to bring to notice additional important issues which you might encounter as you move forward:

However there are several hooks you can use to customise the behaviour:

  1. onBackPressed - to close drawer layout if open

    override fun onBackPressed() {
        if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
          drawerLayout.closeDrawer(GravityCompat.START)
        } else {
          super.onBackPressed()
        }
     }
    
  2. Add navController.addOnNavigatedListener(..) and inside the listener customise HomeAsUpIndicator icon

  3. Override onOptionsItemSelected to customise menu with id android.R.id.home action

  4. Set custom fragment navigator to customise how your fragments are treated (replace or show/ hide)

    navHostFragment = supportFragmentManager
            .findFragmentById(R.id.my_nav_host_fragment) as NavHostFragment? ?: return
    val customNavigator = CustomFragmentNavigator(navHostFragment.requireContext(),
            navHostFragment.childFragmentManager, navHostFragment.id)
    navHostFragment.navController.navigatorProvider.addNavigator(customNavigator)
    
    val inflater = navHostFragment.navController.navInflater
    val graph = inflater.inflate(R.navigation.main_nav_graph)
    navHostFragment.navController.graph = graph
    

Remember navigation arch component is still in alpha, so use it wisely.

Dandruff answered 3/8, 2018 at 4:52 Comment(0)
D
1

If you want, you can use NavController.OnNavigatedListener and use below code to set title.

@Override
public void onNavigated(@NonNull NavController controller, @NonNull NavDestination destination) {
    mActivityBinding.toolbar.setTitle(destination.getLabel());
}

Remember to set label in your navigation graph.

Doriandoric answered 26/9, 2018 at 13:20 Comment(0)
L
0

I think your menu items should be like this:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <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" />

</menu>

Hope this helps

Happy coding...

Lipase answered 3/8, 2018 at 10:57 Comment(0)
B
0

I completely agree with the sentiment here but it is a part of the library

setupActionBarWithNavController

Sets up the ActionBar returned by AppCompatActivity.getSupportActionBar() for use with a NavController.

By calling this method, the title in the action bar will automatically be updated when the destination changes (assuming there is a valid label).

The start destination of your navigation graph is considered the only top level destination. On the start destination of your navigation graph, the ActionBar will show the drawer icon if the given DrawerLayout is non null. On all other destinations, the ActionBar will show the Up button. Call navigateUp(NavController, DrawerLayout) to handle the Up button.

this to me is broken and not the desired result, so what i'm doing is still using it with the navigation view using

    navController = Navigation.findNavController(this, R.id.nav_host);
    NavigationUI.setupWithNavController(navigationView,navController);

and then having a listener to update the title and anything else i want changed like this

   navController.addOnNavigatedListener((controller, destination) -> {
        setToolbarColour(R.color.primary);
        switch (destination.getId()){
            case R.id.dashBoard :
                setToolbarColour(android.R.color.transparent);
                break;
            case R.id.requests :
                toolbar.setTitle(getString(R.string.request));
                break;

        }
    });

I'm then creating a new navigation graph for each of these fragments, not great but i get my intended result

Bulletproof answered 31/10, 2018 at 9:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.