Why does navigation not work in the Navigation Drawer Activity template with version 2.4.1?
tl/dr: You should be following the Tie destinations to menu items documentation and using NavigationUI.onNavDestinationSelected()
to get the right behavior:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
NavController navController = Navigation.findNavController(this,
R.id.nav_host_fragment_content_main);
// By calling onNavDestinationSelected(), you always get the right behavior
return NavigationUI.onNavDestinationSelected(item, navController)
|| super.onOptionsItemSelected(item);
}
Why
Navigation 2.4 uses multiple back stacks associated with each element in your NavigationView
as per the Add a navigation drawer guide:
Starting in Navigation 2.4.0-alpha01, the state of each menu item is saved and restored when you use setupWithNavController.
That means that the 'Home' screen has a back stack associated with it that is restored when you tap on that icon, same for Gallery, Slideshow, and Settings. This is how the state is saved for that item.
This means that each tap on the items in your drawer isn't just navigating to that screen, but is swapping in the entire back stack associated with that item - everything that you've navigated to from that first screen.
So when you call Navigation.findNavController(this, R.id.nav_host_fragment_content_main).navigate(R.id.nav_settings);
, you aren't doing the same thing as selecting the Settings item in your drawer - you're just adding the Settings screen to the 'Home' screen's back stack. This is why tapping on the Home icon doesn't do anything - you're already on the 'Home' screen's back stack.
What you actually want to do is swap to the entirely separate back stack associated with the nav_settings item. This would separate the nav_settings back stack from the Home back stack, thus ensuring that tapping the Home icon takes you back to the Home screen's back stack.
This is exactly what the NavigationUI.onNavDestinationSelected()
API does (as that's exactly what the setupWithNavController
API uses), so you can simply use that directly in your onOptionsItemSelected()
.
However, if you want to manually call navigate()
(which, by the way, means you aren't getting the cross fade animation that you get by default when using onNavDestinationSelected
), you can add the saving state flags to your navigate call by applying NavOptions programmatically:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Bundle bundle =new Bundle();
switch (item.getItemId()) {
case R.id.action_settings:
{
// Manually build the NavOptions that manually do
// what NavigationUI.onNavDestinationSelected does for you
NavOptions navOptions = new NavOptions.Builder()
.setPopUpTo(R.id.nav_home, false, true)
.setRestoreState(true)
.build();
NavController navController = Navigation.findNavController(this,
R.id.nav_host_fragment_content_main);
navController.navigate(R.id.nav_settings, navOptions);
return true;
}
default:
return super.onOptionsItemSelected(item);
}
}
Note that the setupWithNavController
API relies on the nested graph of the current destination to determine which item is selected - the expectation is that all destinations in the 'Home' tab are part of the 'Home' navigation graph. So because you've swapped to the nav_settings, setupWithNavController
assumes you've swapped to that back stack. Since you haven't actually done that, that's why your selected item gets out of sync with which back stack you are on.
Fragment navigation and menu. How to remove latest fragment? Check description
Found the core issue for this problem. The detailed explanation lies in this answer.
For the moment, Is solved it by downgrading the the fragment dependency from 'androidx.navigation:navigation-fragment-ktx:2.4.1'
to
'androidx.navigation:navigation-fragment-ktx:2.3.5'
and now everything works fine.
Bottom navigation bar malfunctions when navigating from a fragment
As @ianhanniballake mentioned in a comment, a similar question was posted here
What I ended up doing was replacing
val action = MainFragmentDirections.actionMainFragmentToReportsFragment()
navController.navigate(action)
with
val item = mainBottomNavigationView.menu.findItem(R.id.reportsFragment)
NavigationUI.onNavDestinationSelected(item, navController)
So basically I used the NavigationUI
API to navigate so that it correctly tracks the back stack. The same NavigationUI
API is being used by the BottomNavigationView
internally
Related Topics
Should Use "Sp" Instead of "Dp" for Text Sizes
Mpandroidchart - Adding Labels to Bar Chart
Bitmap Too Large to Be Uploaded into a Texture
Android Usb Host API and Usb Storage
Connectivity_Action Intent Received Twice When Wifi Connected
Add 'Tools:Replace="Android:Value"' to <Meta-Data> Element at Androidmanifest
Android - Wake Up and Unlock Device
Getting Wifi Signal Strength in Android
How to Prevent Multiple Toast Overlaps
How to Raise a Toast in Asynctask, I am Prompted to Used the Looper
How to Stop/Cancel Android Countdowntimer
Compatiblity of Material Design to Versions Below Android 5.0
Google Play Services Out of Date. Requires 11011000 But Found 10289574