I have an app that uses the androidx navigation api. it works as expected. but i don't understand why the menu button would change from the hamburger icon to the up arrow, since we have a global one available to us. So, I was wondering if it was possible to force the api to keep the hamburger button at all times. I have found that if you use the following code;
val navSet = setOf(R.id.nav_journal, R.id.nav_copyright)
val drawerLayout: DrawerLayout? = findViewById(R.id.drawer_layout)
appBarConfiguration = AppBarConfiguration(navSet, drawerLayout)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
setupActionBarWithNavController(navController, appBarConfiguration)
then the hamburger icon will appear for both activities but it seems unwieldy to maintain a list of possible navigation activities.
Try this:
navController.addOnDestinationChangedListener { controller, destination, _ ->
if (destination.id == R.id.xxx || destination.id == R.id.xxx || ... ) {
supportActionBar?.setHomeAsUpIndicator(R.drawable.hamburger)
}
}
Find Hamburger icon here: https://materialdesignicons.com/
And add this to each of your other Activity or Fragment:
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
requireActivity().drawerLayout.open()
}
return true
}
Try to use setOpenableLayout() to attach the drawerLayout into the appBarConfiguration
This requires to use AppBarConfiguration.Builder pattern:
val appBarConfiguration = AppBarConfiguration.Builder(
R.id.nav_journal, R.id.nav_copyright
).setOpenableLayout(drawerLayout).build()
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController: NavController = navHostFragment.navController
setupActionBarWithNavController(this, navController, appBarConfiguration);
Also make sure you add all the fragments that you don't want to show the up/back button in, into the appBarConfiguration. You already added a couple of them (R.id.nav_journal, R.id.nav_copyright), if you have more fragments in the drawer, then you need to add them as well.
Related
I am working on adding a toolbar title in the NavGraph. I used android:label"sample tile" in the NavGraph, but its not updating the toolbar with the label.
Tried using android:label in the NavGraph, but its not updating the title of the toolbar.
Using android:label alone won't be fully integrated with jetpack navigation.
In documentation:
NavigationUI contains methods that automatically update content in
your top app bar as users navigate through your app. For example,
NavigationUI uses the destination labels from your navigation graph to
keep the title of the top app bar up-to-date.
So, you need to configure the top app bar to work with NavigationUI and this is done through setupWithNavController().
This section in documentations provides an example:
// Kotlin
override fun onCreate(savedInstanceState: Bundle?) {
setContentView(R.layout.activity_main)
...
val navController = findNavController(R.id.nav_host_fragment)
val appBarConfiguration = AppBarConfiguration(navController.graph)
findViewById<Toolbar>(R.id.toolbar)
.setupWithNavController(navController, appBarConfiguration)
}
// Java
#Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
...
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
AppBarConfiguration appBarConfiguration =
new AppBarConfiguration.Builder(navController.getGraph()).build();
Toolbar toolbar = findViewById(R.id.toolbar);
NavigationUI.setupWithNavController(
toolbar, navController, appBarConfiguration);
}
I've got an issue concerning the implementation of a Drawer Layout with Navigation component.
I have created the drawer layout using the include Navigation Drawer Activity of Android Studio.
Actually, all is fine if the menu items purpose is to change fragments or activity (like programs, songs, settings etc ... on the screenshot) define in the navigation XML
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
val navView: NavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(
setOf(
R.id.nav_user_programs_list,
R.id.nav_user_songs_list,
R.id.nav_user_settings,
R.id.nav_user_legal_notices,
R.id.nav_games
), drawerLayout
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
However, I also would like to execute a logout action on the "logout" menu item without launching another fragment or activity :
I managed to do it like that :
navView.setNavigationItemSelectedListener {
if (it.itemId == R.id.nav_logout) {
logoutUser()
}
true
}
But my problem is : With that method, all the other items which used to work (changing fragment) don't work anymore because it called the NavigationItemSelectedListener which do nothing in that case.
Is there a solution to combine the both method ? :
Changing fragment with the default drawer layout of android studio
Using a NavigationItemSelectedListener to only execute an action on only one menuitem.
I hope it's clear enough. Don't hesitate if you need precisions.
Thank you very much.
Solution
Ok, I figured it out, this is what the framework is calling for you:
NavigationUI.onNavDestinationSelected(dest, navController)
So you can do the same for all other cases:
navView.setNavigationItemSelectedListener {dest ->
when(dest.itemId) {
R.id.logout -> logout()
else -> NavigationUI.onNavDestinationSelected(dest, navController)
}
true
}
Update
The above stops "automatically closing" the drawer, so..
navView.setNavigationItemSelectedListener {dest ->
when(dest.itemId) {
else -> {
NavigationUI.onNavDestinationSelected(dest, navController)
drawerLayout.closeDrawers()
}
}
true
}
I am using a navigation graph in my app. I have a bottom navigation bar. I am staring my navigation components with the following in my Main Activity:
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
val appBarConfiguration = AppBarConfiguration(setOf(
R.id.navigation_artist_list, R.id.navigation_dashboard, R.id.navigation_notifications))
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
This starts my navigation components, which inflate the fragement setup in R.id.navigation_artist_list.
From an observer in that fragment I am navigating to another fragment with:
viewModel.selectedAlbum.observe(this, Observer { artist ->
val action = HomeFragmentDirections.actionNavigationHomeToAlbumFragment(artist)
root.findNavController().navigate(action)
})
However, none of the mechanisms available for navigation work properly. The back button just refreshes the fragment and populates the list in the fragment again. The back button on the action bar is completely ignored.
Do I need to do something else to have the proper back button behavior, to go back to the previous fragment?
In yout MainActivity, override the onSupportNavigateUp() method to call navigateUp() in the navigation controller
override fun onSupportNavigateUp(): Boolean {
val navController = this.findNavController(R.id.nav_host_fragment)
return navController.navigateUp()
}
Is it possible to use Android Navigation component/graph in the single activity app in which each fragment has its own toolbar?
Also, container activity has a navigation drawer that needs setup with toolbar and navigation controller, but at the time of activity creation I don't have yet the toolbar.
I am using this code (called in onCreate)
private fun setupNavigation() {
// val toolbar = findViewById<Toolbar>(R.id.toolbar);
// setSupportActionBar(toolbar);
// supportActionBar!!.setDisplayHomeAsUpEnabled(true);
// supportActionBar!!.setDisplayShowHomeEnabled(true);
val drawerLayout = findViewById<DrawerLayout>(R.id.drawer_layout);
val navigationView = findViewById<NavigationView>(R.id.drawer_navigation_view);
val navController = Navigation.findNavController(this, R.id.navigation_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)
NavigationUI.setupWithNavController(navigationView, navController)
navigationView.setNavigationItemSelectedListener(this)
}
But since I don't have toolbar at the time it throws an error (ActionBar.setTitle called on a null object).
Is it possible to have this, or I need to drop the idea to use navigation component in this case?
The only requirement is that you call setupActionBarWithNavController after you call setSupportActionBar(). If you're doing that in your Fragment, then just call setupActionBarWithNavController directly after that.
For example, in your Fragment:
private fun onViewCreated(view: View, bundle: savedInstanceState) {
val toolbar = view.findViewById<Toolbar>(R.id.toolbar);
// Set the Toolbar as your activity's ActionBar
requireActivity().setSupportActionBar(toolbar);
// Find the activity's DrawerLayout
val drawerLayout = requireActivity().findViewById<DrawerLayout>(R.id.drawer_layout);
// Find this Fragment's NavController
val navController = NavHostFragment.findNavController(this);
// And set up the ActionBar
NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)
}
There's also a separate NavigationUI.setupWithNavController() method that takes a Toolbar. This would be appropriate if you aren't actually using any of the other ActionBar APIs.
I've been building a sample app with the new Navigation Architecture Component in combination with a Nav Drawer.
I have my navigation graph created, my fragments created, and the nav drawer displaying and navigating between the fragments mostly as expected. The problem is that each time I select an item from the nav drawer, it adds the fragment to the stack, instead of popping the existing one and adding the new one. This means that if I navigate to a new fragment, I've created a back stack and tapping the menu button in the action bar pops the latest fragment off the stack, instead of opening the nav drawer as I would expect. Here is my code:
private fun configureNavigation() {
navDrawerLayout = findViewById(R.id.navigation_drawer_layout)
navView = findViewById(R.id.navigationView)
navController = Navigation.findNavController(this, R.id.nav_host_fragment)
appBarConfiguration = AppBarConfiguration(
setOf(R.id.workouts_fragment, R.id.create_workout_fragment, R.id.workout_history_fragment),
navDrawerLayout
)
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration)
NavigationUI.setupWithNavController(navView, navController)
navView.setNavigationItemSelectedListener(this)
}
override fun onSupportNavigateUp() = navController.navigateUp()
override fun onNavigationItemSelected(menuItem: MenuItem): Boolean {
menuItem.isChecked = true
navDrawerLayout.closeDrawers()
#IdRes val destination: Int = when (menuItem.itemId) {
R.id.workouts_nav_drawer_item -> R.id.workouts_fragment
R.id.create_workout_nav_drawer_item -> R.id.create_workout_fragment
R.id.workout_history_nav_drawer_item -> R.id.workout_history_fragment
else -> {
throw IllegalArgumentException("Attempting to process an unrecognized menuItem id in the navigation drawer layout")
}
}
if (destination != currentDestination) {
currentDestination = destination
navController.navigate(destination)
}
return true
}
I discovered there were two requirements to get the nav drawer, nav graph, and action bar completely in sync with my desired behaviour.
The first is the AppBarConfiguration. I had to create an app bar configuration containing a set of top level destinations (the top level fragments in the nav drawer).
The second aspect was to make sure that in onSupportNavigateUp() function to include the app bar configuration in the call as such: `navController.navigateUp(appBarConfiguration).
Once I had done these two things, everything worked as expected, and the nav drawer, action bar, and up button all worked in sync without unnecessarily adding fragments to the stack.