31

I have the following Activity

class MainActivity : AppCompatActivity() {

private lateinit var drawerLayout: androidx.drawerlayout.widget.DrawerLayout

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

    drawerLayout = drawer_layout

    val navController = Navigation.findNavController(this, R.id.fragment_main_navHost)

    setSupportActionBar(toolbar)

    NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)
    navView_main.setupWithNavController(navController)
}

override fun onSupportNavigateUp(): Boolean {
    return NavigationUI.navigateUp(drawerLayout,
        Navigation.findNavController(this, R.id.fragment_main_navHost))
}

override fun onBackPressed() {
    if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
        drawerLayout.closeDrawer(GravityCompat.START)
    } else {
        super.onBackPressed()
    }
}

which as you can see is associated with navigation graph, and I am using a navigation drawer. When I am navigating through the items in the drawer I want to keep the hamburger icon, and only change it to up/back button when I click on an item within the fragment or popup for example and ensure that the behavior of the system reflects what the user expects based on the icon displayed. Is that possible

Jitendra A
  • 1,568
  • 10
  • 18
Mustafa ALMulla
  • 870
  • 2
  • 10
  • 23

3 Answers3

27

To control when the AppBar navigation up/back show the following need to be done

1- create AppBarConfiguration and pass to it the top level destination and drawerLayout

    appBarConfiguration = AppBarConfiguration(
        setOf(
            R.id.dest_one,
            R.id.dest_two
        ),
        drawerLayout
    )

2- Tell the AppBar about the configration and navigation. this will help to show a title and show up arrow or drawer menu icon

setupActionBarWithNavController(navController, appBarConfig)

3- Finally override the onOptionsItemSelected and onSupportNavigateUp and the Navigation Component extension to inform the AppBar how to behave

    override fun onOptionsItemSelected(item: MenuItem)= item.onNavDestinationSelected(findNavController(R.id.my_nav_host_fragment))
        || super.onOptionsItemSelected(item)


override fun onSupportNavigateUp() = findNavController(R.id.my_nav_host_fragment).navigateUp(appBarConfiguration)

Reference Google Code Lab Navigation Navigation Codelab

Mustafa ALMulla
  • 870
  • 2
  • 10
  • 23
11

Follow this steps

1. Bind your NavigationView with NavigationUI

NavigationUI.setupWithNavController(nav_view, hostFragment.navController)

2. Bind ActionBar With NavController

NavigationUI.setupActionBarWithNavController(this@NavActivity, hostFragment.navController)

3. Bind ActionBar and DrawerLayout With NavController

NavigationUI.setupActionBarWithNavController(this@NavActivity, hostFragment.navController, drawer_layout)

4. override onSupportNavigateUp() in your activity

override fun onSupportNavigateUp(): Boolean {
    return NavigationUI.navigateUp(drawer_layout, hostFragment.navController) 
              || super.onSupportNavigateUp()
}

Sample:

class NavActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {

    lateinit var hostFragment: NavHostFragment

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_nav)
        setSupportActionBar(toolbar)

        fab.setOnClickListener { view ->
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show()
        }

        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)

        hostFragment = supportFragmentManager.findFragmentById(R.id.my_nav_host_fragment) as NavHostFragment    
        NavigationUI.setupWithNavController(nav_view, hostFragment.navController)    
        NavigationUI.setupActionBarWithNavController(this@NavActivity, hostFragment.navController)    
        NavigationUI.setupActionBarWithNavController(this@NavActivity, hostFragment.navController, drawer_layout)    
    }

    override fun onBackPressed() {
        if (drawer_layout.isDrawerOpen(GravityCompat.START)) {
            drawer_layout.closeDrawer(GravityCompat.START)
        } else {
            super.onBackPressed()
        }
    }

    override fun onSupportNavigateUp(): Boolean {
        return NavigationUI.navigateUp(drawer_layout, hostFragment.navController) || super.onSupportNavigateUp()
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        // Inflate the menu; this adds items to the action bar if it is present.
        menuInflater.inflate(R.menu.nav, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        when (item.itemId) {
            R.id.action_settings -> return true
            else -> return super.onOptionsItemSelected(item)
        }
    }

    override fun onNavigationItemSelected(item: MenuItem): Boolean {
        // Handle navigation view item clicks here.
        drawer_layout.closeDrawer(GravityCompat.START)
        return true
    }
}

Output

Home Fragment:

Home Fragment

Fragment Two:

Fragment Two

Fragment Tree:

Fragment Tree

crgarridos
  • 8,758
  • 3
  • 49
  • 61
AskNilesh
  • 67,701
  • 16
  • 123
  • 163
  • 1
    Thank you for trying, but what you have provided has steps that are not required, and has produced the same result. I should be able to control what icon I want to display when navigating using the navigation drawer. – Mustafa ALMulla Sep 11 '18 at 16:46
  • 1
    in principle, the navigation drawer should have lateral navigation which means the items are at the same level. So when I am moving from one item to another using the drawer the fragment should be replaced not added on top of the stack. Please see https://material.io/design/navigation/understanding-navigation.html#lateral-navigation. One last thing, all you need is step three, and 4 if you want to have the arch component control the back and up behavior – Mustafa ALMulla Sep 12 '18 at 16:10
  • 3
    When I click on back arrow, it displays navigation drawer instead of going back to previous fragment. – AVEbrahimi Jan 07 '19 at 06:35
  • @AVEbrahimi haveu `override` the `onSupportNavigateUp()` method like in my above code – AskNilesh Jan 07 '19 at 06:36
  • When I click on back arrow, it displays navigation drawer instead of going back to previous fragment. – Is there any solution for this,I am not looking for a work around – Abraham Mathew May 20 '19 at 07:27
  • When I click on back arrow, it displays navigation drawer instead of going back to previous fragment. Just add toolbar.setupWithNavController(navController) – Anwar Zahid Jul 26 '21 at 07:36
3

So, I think that you can use the NavController.OnNavigatedListener to listen wich fragment will be shown, and then update de toolbar icon.

val navController = Navigation.findNavController(this, R.id.fragment_main_navHost)
navController.addOnNavigatedListener(contoller, destination -> {
   if(destination.id == R.id.fragmentTwo){
          // change the toolbar icon here
}
    })

Sorry, I have no computer here, so I write this code without any IDE, this can have error. But take the idea.

Hope this help you.

Pedro Massango
  • 4,114
  • 2
  • 28
  • 48
  • Though your answer could be a workaround I was hoping for a solution that automates this process. This will require keeping track of the fragments to know which action to perform. With this I will lose the benefits of the Navigation Arch Component. – Mustafa ALMulla Sep 11 '18 at 18:37