Handle onBackPressed in Android Navigation Component - android

I have implemented navigation Drawer with Navigation Components in Android. I have 5 fragments that I want to go back to my HomeFragment when I click on back pressed. For the moment they stay onBackStack and do not go to my desired fragment but go to whatever fragment was first.
This is my nav_graph :
<?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/setupFragment"
android:id="#+id/treasure_nav"
android:label="Pick a country">
<fragment android:id="#+id/homeFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.home.HomeFragment"
android:label="Home"
tools:layout="#layout/fragment_home">
<action android:id="#+id/action_home_fragment_to_namesFragment2"
app:popUpTo="#id/homeFragment"
app:destination="#id/namesFragment"/>
<action android:id="#+id/action_home_fragment_to_quranFragment"
app:popUpTo="#id/homeFragment"
app:destination="#id/quranFragment"/>
<action android:id="#+id/action_homeFragment_to_tasbeehFragment"
app:popUpTo="#id/homeFragment"
app:destination="#id/tasbeehFragment"/>
<action android:id="#+id/action_homeFragment_to_galleryFragment"
app:popUpTo="#id/homeFragment"
app:destination="#id/galleryFragment"/>
<action android:id="#+id/action_homeFragment_to_newsFragment"
app:popUpTo="#id/homeFragment"
app:destination="#id/newsFragment"/>
<action android:id="#+id/action_homeFragment_to_settingsFragment"
app:popUpTo="#id/homeFragment"
app:destination="#id/settingsFragment"/>
</fragment>
<fragment
android:id="#+id/namesFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.names.NamesFragment"
android:label="Names of Allah"
tools:layout="#layout/fragment_names"/>
<fragment
android:id="#+id/quranFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.quran.QuranFragment"
android:label="Quran"
tools:layout="#layout/fragment_quran"/>
<fragment android:id="#+id/tasbeehFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.tasbeeh.TasbeehFragment"
android:label="Tasbeeh"
tools:layout="#layout/fragment_tasbeeh"/>
<fragment android:id="#+id/galleryFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.gallery.GalleryFragment"
android:label="Gallery"
tools:layout="#layout/fragment_gallery"/>
<fragment android:id="#+id/newsFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.news.NewsFragment"
android:label="News"
tools:layout="#layout/fragment_news"/>
<fragment android:id="#+id/settingsFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.settings.SettingsFragment"
android:label="Settings"
tools:layout="#layout/fragment_settings"/>
<fragment android:id="#+id/setupFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.setup.SetupFragment"
android:label="Pick country"
tools:layout="#layout/fragment_setup">
<action android:id="#+id/action_setupFragment_to_homeFragment3"
app:destination="#+id/homeFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/treasure_nav"
app:popUpToInclusive="true"/>
</fragment>
</navigation>
And this is my onBackPressed in my MainActivity (and the only one) :
override fun onBackPressed() {
if (drawer_layout.isDrawerOpen(GravityCompat.START)) {
drawer_layout.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
Edit: When i remove the super.onBackPressed() and replace it with :
findNavController(R.id.nav_host_fragment).popBackStack(R.id.homeFragment, false) I achieve what I want. The only problem is that when I am in the homeFragment I want to end the app but I can't.

If my understanding is correct, you want to go back to HomeFragment wherever you are in the navigation flow. For this case you could try registering OnBackPressedCallback on your Fragments via addOnBackPressedCallback, and call popBackStack to navigate to your HomeFragment. Try adding this to Fragments' onViewCreated that need to go back to HomeFragment on backpress:
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
navController = Navigation.findNavController(view);
requireActivity().addOnBackPressedCallback(getViewLifecycleOwner(), () -> {
navController.popBackStack(R.id.homeFragment, false);
});
return true;
});

If you want to close app when press back in HomeFragment, it's just specified these attributes of the last action that navigates you to this destination:
app:popUpToInclusive to true
app:popUpTo to of the last fragment(SetupFragment) that navigates you here(HomeFragment)
It means change your code like this:
<fragment android:id="#+id/setupFragment"
android:name="com.stavro_xhardha.pockettreasure.ui.setup.SetupFragment"
android:label="Pick country"
tools:layout="#layout/fragment_setup">
<action android:id="#+id/action_setupFragment_to_homeFragment3"
app:destination="#+id/homeFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/setupFragment" // this line changes
app:popUpToInclusive="true" /> // and this requires too
</fragment>

Related

Android Navigation Component - error No destination with ID

I have problem with navigation component.
I have 2 nav graph: nav1 and nav2
In nav2 start destination by default is FragmentA, but sometimes I need to start from FragmentB. I use this code (this code in MainActivity):
if(isTest) {
val navController = findNavController(navigationFragment())
val navGraph = navController.navInflater.inflate(R.navigation.nav)
navGraph.startDestination = R.id.fragmentB
findNavController(navigationFragment()).graph = navGraph
}
But there is error like this -> java.lang.IllegalArgumentException: No destination with ID 2131362873 is on the NavController's back stack
nav2 code here:
android:id="#+id/nav2"
app:startDestination="#id/FragmentA">
<fragment
android:id="#+id/FragmentA"
android:name="......./FragmentA"
android:label="FragmentA" />
<fragment
android:id="#+id/FragmentB"
android:name="......./FragmentB"
android:label="FragmentB" />
From nav1 action:
<action
android:id="#+id/action_global_link_test"
app:destination="#id/nav2"
app:enterAnim="#anim/enter_from_right"
app:exitAnim="#anim/exit_from_left"
app:popEnterAnim="#anim/enter_from_left"
app:popExitAnim="#anim/exit_from_right" />

Android Navigation Component: back button pressed multiple times before it closes the app

I have a navigation graph with 4 fragments. Each fragment is a stand-alone view: can only navigate to the next fragment, but never navigate back to the previous one.
Expected behaviour:
Pressing back button should close the app. If the fragment has a pager, then the back button should navigate back the pager until it reaches index 0, then after that should close the app.
SplashFragment: a splash screen
LanguageFragment: a view to setup language
BoardingFragment: has a pager and serves as an on-boarding flow
StartupFragment: has a pager and serves as an initial set up flow
The Problem:
All works fine from Frag1 (SplashFragment) to Frag3 (BoardingFragment). But once navigated to Frag4 (StartupFragment) and pager is at index 0, I still have to press back button 7 times before it closes the app. No crashes nor error thrown.
<?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"
android:id="#+id/splash_nav_graph"
app:startDestination="#id/splash_fragment">
<fragment
android:id="#+id/splash_fragment"
android:name=".ui.fragments.SplashFragment"
android:label="fragment_splash"
tools:layout="#layout/fragment_splash" >
<action
android:id="#+id/splash_to_lang_action"
app:destination="#id/language_fragment"
app:popUpTo="#id/splash_fragment"
app:popUpToInclusive="true"/>
</fragment>
<fragment
android:id="#+id/language_fragment"
android:name=".ui.fragments.LanguageFragment"
android:label="fragment_language"
tools:layout="#layout/fragment_language" >
<action
android:id="#+id/lang_to_boarding_action"
app:destination="#id/boarding_fragment"
app:popUpTo="#id/splash_nav_graph"
app:popUpToInclusive="true"/>
</fragment>
<fragment
android:id="#+id/boarding_fragment"
android:name=".ui.fragments.BoardingFragment"
android:label="fragment_boarding"
tools:layout="#layout/fragment_boarding" >
<action
android:id="#+id/boarding_to_startup_action"
app:destination="#id/startup_fragment"
app:popUpTo="#id/splash_nav_graph"
app:popUpToInclusive="true"/>
</fragment>
<fragment
android:id="#+id/startup_fragment"
android:name=".ui.fragments.StartupFragment"
android:label="fragment_startup"
tools:layout="#layout/fragment_startup" />
</navigation>
In SplashFragment:
// navigate after an action
navController.navigate(
R.id.splash_to_lang_action, null, null,
FragmentNavigatorExtras(binding.logo to binding.logo.transitionName)
)
In LanguageFragment:
// navigate after an action
navController.navigate(R.id.lang_to_boarding_action)
In BoardingFragment:
// navigate after an action
navController.navigate(R.id.boarding_to_startup_action)
// manage back-press for pager
activity?.onBackPressedDispatcher?.addCallback {
if (binding.uspPager.currentItem != 0) binding.uspPager.currentItem--
else {
isEnabled = false
activity?.onBackPressed()
}
}
In StartupFragment:
// manage back-press for pager
activity?.onBackPressedDispatcher?.addCallback {
if (binding.uspPager.currentItem != 0) binding.uspPager.currentItem--
else {
isEnabled = false
activity?.onBackPressed()
}
}
I appreciate any help to the max. Thanks in advance.

OnBackPressed does not work correctly in some fragments

In MainActivity I have next method:
override fun onBackPressed() {
val onBackPressListener = currentFragmentOnTop() as? OnBackPressListener
if (onBackPressListener?.onBackPress() != true) super.onBackPressed()
}
But sometimes he doesn't work correctly.
For example:
In my startDestination fragment I call dialog(bottomMenuDialog) with menuItems, by clicking on which I call fragments:
findNavController().navigate(R.id.settings_list)
Here is code of my nav_graph.xml:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/nav_graph"
app:startDestination="#id/authorizationsListFragment">
<fragment
android:id="#+id/authorizationsListFragment"
android:name="com.mandarine.sai.features.authorizations.list.AuthorizationsListFragment"
android:label="AuthorizationsListFragment">
<action
android:id="#+id/settings_list"
app:destination="#+id/settingsListFragment" />
<action
android:id="#+id/connections_list"
app:destination="#+id/connectionsListFragment" />
<action
android:id="#+id/bottom_menu_dialog"
app:destination="#+id/bottomMenuDialog" />
</fragment>
But I can't click on the back button to go to the start fragment.
Behavior: it opens the start fragment and immediately goes back to the current one, to settings_list.
override fun onBackPressed() {
finish()
}

NavController destination unknown using BottomNavigation

I'm using BottomNavigation in the main menu class to cycle through Fragments in the main screen. The weird thing about this is my app crashes because NavController doesn't find the destination in one of four fragments. MainMenuFragment is not the same as MainMenu (activity containing the fragment and NavHostFragment). Any idea why this is happening?
NavGraph: http://prntscr.com/pcfufk
Activity (MainMenu.java) screen http://prntscr.com/pcgcnv
In MainMenu.java:
private BottomNavigationView.OnNavigationItemSelectedListener listener = new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem menuItem) {
Bundle args = new Bundle();
switch (menuItem.getItemId()) {
case R.id.addorder:
args.putString("UID", UID);
controller.navigate(R.id.action_mainMenuFragment_to_addOrderFragment, args);
break;
case R.id.addcust:
bundle.putString("UID", UID);
controller.navigate(R.id.action_mainMenuFragment_to_addCustomerFragment);
break;
case R.id.barcodeScan:
controller.navigate(R.id.action_mainMenuFragment_to_addProductFragment);
break;
case R.id.home:
bundle.putString("UID", UID);
if (controller.getCurrentDestination() != null && controller.getCurrentDestination().getId() == R.id.mainMenuFragment) {
controller.navigate(R.id.mainMenuFragment, bundle);
}
break;
}
return true;
}
};
onCreate in MainMenu.java:
bundle.putString("UID", UID);
controller = Navigation.findNavController(this, R.id.fragment_container);
controller.navigate(R.id.mainMenuFragment, bundle);
NavGraph xml
<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"
android:id="#+id/navigation_mainmenu"
app:startDestination="#id/mainMenuFragment">
<fragment
android:id="#+id/addCustomerFragment"
android:name="com.google.android.gms.samples.vision.barcodereader.fragments.customer.addCustomerFragment"
android:label="addcustomerfragment"
tools:layout="#layout/addcustomerfragment" />
<fragment
android:id="#+id/mainMenuFragment"
android:name="com.google.android.gms.samples.vision.barcodereader.fragments.mainmenu.mainMenuFragment"
android:label="mainmenufragment"
tools:layout="#layout/mainmenufragment">
<action
android:id="#+id/action_mainMenuFragment_to_addProductFragment"
app:destination="#id/addProductFragment"
app:enterAnim="#anim/fui_slide_in_right"
app:exitAnim="#anim/fui_slide_out_left"
app:popExitAnim="#anim/fui_slide_out_left"
app:popUpTo="#+id/mainMenuFragment" />
<action
android:id="#+id/action_mainMenuFragment_self"
app:destination="#id/mainMenuFragment" />
<action
android:id="#+id/action_mainMenuFragment_to_addOrderFragment"
app:destination="#id/addOrderFragment"
app:enterAnim="#anim/fui_slide_in_right"
app:exitAnim="#anim/fui_slide_out_left"
app:popUpTo="#+id/mainMenuFragment" />
<argument
android:name="UID"
app:argType="string"
app:nullable="false" />
<action
android:id="#+id/action_mainMenuFragment_to_addCustomerFragment"
app:destination="#id/addCustomerFragment"
app:enterAnim="#anim/fui_slide_in_right"
app:exitAnim="#anim/fui_slide_out_left"
app:popUpTo="#+id/navigation_mainmenu" />
</fragment>
<fragment
android:id="#+id/addProductFragment"
android:name="com.google.android.gms.samples.vision.barcodereader.fragments.products.addProductFragment"
android:label="addproductfragment"
tools:layout="#layout/addproductfragment" />
<fragment
android:id="#+id/additionalInfoFragment"
android:name="com.google.android.gms.samples.vision.barcodereader.fragments.orders.additionalInfoFragment"
android:label="addinfofragment"
tools:layout="#layout/addinfofragment">
<argument
android:name="UID"
app:argType="string" />
</fragment>
<fragment
android:id="#+id/addOrderFragment"
android:name="com.google.android.gms.samples.vision.barcodereader.fragments.orders.addOrderFragment"
android:label="choosecustomerfragment"
tools:layout="#layout/choosecustomerfragment">
<action
android:id="#+id/action_addOrderFragment_to_additionalInfoFragment"
app:destination="#id/additionalInfoFragment"
app:enterAnim="#anim/fui_slide_in_right"
app:exitAnim="#anim/fui_slide_out_left"
app:popUpTo="#+id/mainMenuFragment" />
<argument
android:name="UID"
app:argType="string" />
</fragment>
Using BottomNavigationView with NavigationUI using NavigationUI.setupWithNavigationController(controller) should solve the problem.
Links to sources:
https://medium.com/#vepetruskova/the-new-android-in-app-navigation-f7bfbe925b9
https://developer.android.com/guide/navigation/navigation-ui

Navigatoin Graph issue when add activity

I am using Navigation Android component with navigation drawer, its working fine our code :
private void setupNavMenu() {
NavHeaderMainBinding navHeaderMainBinding = DataBindingUtil.inflate(getLayoutInflater(),
R.layout.nav_header_main, mActivityMainBinding.navigationView, false);
mActivityMainBinding.navigationView.addHeaderView(navHeaderMainBinding.getRoot());
navHeaderMainBinding.setViewModel(mMainViewModel);
addNavigationItem();
NavigationUI.setupActionBarWithNavController(this, navController, mActivityMainBinding.drawerView);
mNavigationView.setNavigationItemSelectedListener(
item -> {
mDrawer.closeDrawer(GravityCompat.START);
return true;
});
NavigationUI.setupWithNavController(mNavigationView, navController);
}
and this is our navigation item:
private void addNavigationItem() {
final Menu menu = mNavigationView.getMenu();
menu.add(R.id.group, R.id.test1Fragment, Menu.NONE, "test1");
menu.add(R.id.group, R.id.test2Fragment Menu.NONE, "test2");
menu.add(R.id.group, R.id.testActivity3 Menu.NONE, "test3");
}
navigation_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"
android:id="#+id/home_fragment"
app:startDestination="#id/test1Fragment">
<fragment
android:id="#+id/test1Fragment"
android:name="com.test.Test1"
android:label="FragmentOne">
<action
android:id="#+id/action_test1"
app:destination="#id/test2Fragment" />
</fragment>
<fragment
android:id="#+id/test2Fragment"
android:name="com.test.Test2"
android:label="FragmentThree" >
<action
android:id="#+id/action_test2"
/>
</fragment>
<activity
android:id="#+id/testActivity3"
android:name="com.test.TestActivity"
android:label="Act"
tools:layout="#layout/activity_test"
></activity>
</navigation>
everything is working fine.Now issue is that when i click on second tag in navigation drawer and from the second fragment again open drawer and click on third tab which is Activity. then on back press stack not working properly,fragment test1 and test2 overlapped each other.How could i resolved this issue? i think backstack not working fine if i add Activity inside navigation graph.

Categories

Resources