NavController destination unknown using BottomNavigation - android

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

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

Too many arguments for public final fun actionConformationFragmentToHomeFragment()

I want to send my arguments to another fragment using NavDirections
<fragment
android:id="#+id/conformationFragment"
android:name="com.example.panoramic.ui.ConformationFragment"
android:label="ConformationFragment"
tools:layout="#layout/fragment_conformation">
<action
android:id="#+id/action_conformationFragment_to_registerProductFragment"
app:destination="#id/registerProductFragment" />
<action
android:id="#+id/action_conformationFragment_to_homeFragment"
app:destination="#id/homeFragment" />
<argument
android:name="modelNumber"
app:argType="string" />
<argument
android:name="serialNumber"
app:argType="string" />
</fragment>
Here is how I attempt to send arguments:
class ConformationFragment : Fragment(R.layout.fragment_conformation) {
private var fragmentConformationBinding: FragmentConformationBinding? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = FragmentConformationBinding.bind(view)
fragmentConformationBinding = binding
val modelNumber = binding.modelValue.text.toString()
val serialNumber = binding.serialValue.text.toString()
binding.editingInformationButton.setOnClickListener {
findNavController().navigate(R.id.action_conformationFragment_to_registerProductFragment)
}
binding.confirmButton.setOnClickListener {
findNavController().navigate(ConformationFragmentDirections
.actionConformationFragmentToHomeFragment(modelNumber, serialNumber))
}
}
override fun onDestroyView() {
fragmentConformationBinding = null
super.onDestroyView()
}
}
I am sure dependencies are correct, but still, the error appears:
Too many arguments for public final fun actionConformationFragmentToHomeFragment(): NavDirections defined in com.example.panoramic.ui.ConformationFragmentDirections.Companion
and red line under modelNumber and serialNumber in actionConformationFragmentToHomeFragment() function
I checked other similar question but none of them works for me.
The issue is that the arguments you defined in your navigation graph file are accepted by ConformationFragment and not by HomeFragment.
Arguments should be defined under <fragment> tag of a fragment that is accepting defined arguments, so every other navigation action to this fragment would accept arguments. This is how the navigation graph should be declared regarding ConformatioFragment and HomeFragment:
<fragment
android:id="#+id/conformationFragment"
android:name="com.example.panoramic.ui.ConformationFragment"
android:label="ConformationFragment"
tools:layout="#layout/fragment_conformation">
<action
android:id="#+id/action_conformationFragment_to_registerProductFragment"
app:destination="#id/registerProductFragment" />
<action
android:id="#+id/action_conformationFragment_to_homeFragment"
app:destination="#id/homeFragment" />
</fragment>
<fragment
android:id="#+id/homeFragment"
android:name="com.example.panoramic.ui.HomeFragment"
android:label="HomeFragment"
tools:layout="#layout/fragment_home">
<argument
android:name="modelNumber"
app:argType="string" />
<argument
android:name="serialNumber"
app:argType="string" />
</fragment>
After these changes, you should be able to pass arguments into actionConformationFragmentToHomeFragment() function.

Handle onBackPressed in Android Navigation Component

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>

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.

How to navigate from nested Fragment to parent fragment using Jetpack Navigation?

I have main navigation:
SplashFragment -> RegistrationFragment -> RootFragment
<fragment
android:id="#+id/splashFragment"
android:name="com.low6.low6.features.splash.SplashFragment"
android:label="Splash"
tools:layout="#layout/fragment_splash" >
<action
android:id="#+id/action_next"
app:clearTask="true"
app:destination="#id/registrationFragment" />
</fragment>
<fragment
android:id="#+id/registrationFragment"
android:name="com.low6.low6.features.registration.RegistrationFragment"
android:label="Register">
<action
android:id="#+id/action_next"
app:clearTask="true"
app:destination="#id/rootFragment" />
</fragment>
<fragment
android:id="#+id/rootFragment"
android:name="com.low6.low6.core.RootFragment"
android:label="#string/home"
tools:layout="#layout/fragment_root" />
And I have nested registration navigation:
RegistrationPersonalFragment -> RegistrationContactFragment -> RegistrationSecurityFragment
<fragment
android:id="#+id/registrationPersonalFragment"
android:name="com.low6.low6.features.registration.RegistrationPersonalFragment"
android:label="Register">
<action
android:id="#+id/action_next"
app:destination="#+id/registrationContactFragment" />
</fragment>
<fragment
android:id="#+id/registrationContactFragment"
android:name="com.low6.low6.features.registration.RegistrationContactFragment"
android:label="Register">
<action
android:id="#+id/action_next"
app:destination="#+id/registrationSecurityFragment" />
</fragment>
<fragment
android:id="#+id/registrationSecurityFragment"
android:name="com.low6.low6.features.registration.RegistrationSecurityFragment"
android:label="Register">
<action
android:id="#+id/action_next"
app:destination="#+id/rootFragment" />
</fragment>
How to redirect from the last nested RegistrationSecurityFragment to RootFragment using Jetpack Navigation component?
Currently
<action
android:id="#+id/action_next"
app:destination="#+id/rootFragment" />
And
navigateTo(R.id.action_next)
Gives me
java.lang.IllegalArgumentException: navigation destination com.xxx:id/rootFragment referenced from action com.xxx:id/action_next is unknown to this NavController
at androidx.navigation.NavController.navigate(NavController.java:691)
at androidx.navigation.NavController.navigate(NavController.java:648)
at androidx.navigation.NavController.navigate(NavController.java:634)
at com.xxx.core.BaseFragment.navigateTo(BaseFragment.kt:73)
at com.xxx.core.BaseFragment.navigateTo$default(BaseFragment.kt:66)
at com.xxx.features.registration.RegistrationSecurityFragment$epoxyController$1$$special$$inlined$button$lambda$1.onClick(RegistrationSecurityFragment.kt:106)
at android.view.View.performClick(View.java:6597)
at android.view.View.performClickInternal(View.java:6574)
at android.view.View.access$3100(View.java:778)
at android.view.View$PerformClick.run(View.java:25885)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
If you have more than one navigation graph, please make sure you're using the right navigation controller. Using Navigation.findNavController(view) in some cases you might need to get your root view to get the root's navigation. Hope, this'll help.
When you have nested NavControllers, findNavController() return only last. To get previous navControllers, you can traverse up using parentFragment property.
Extensions with this approach:
// find all nav controllers from closest to farest
fun Fragment.findAllNavControllers(): List<NavController> {
val navControllers = mutableListOf<NavController>()
var parent = parentFragment
while (parent != null) {
if (parent is NavHostFragment) {
navControllers.add(parent.navController)
}
parent = parent.parentFragment
}
return navControllers
}
// find one nav controller by fragment id
fun Fragment.findNavControllerById(#IdRes id: Int): NavController {
var parent = parentFragment
while (parent != null) {
if (parent is NavHostFragment && parent.id == id) {
return parent.navController
}
parent = parent.parentFragment
}
throw RuntimeException("NavController with specified id not found")
}
And usage:
findAllNavControllers()[2]
findNavControllerById(R.id.navHostFragment)
In your code, you can pass the resource ID of the global action to the navigate() method for each UI element.
your_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Navigation.findNavController(view).navigate(R.id.main_fragment);
}
});
Thanks to #Vladimir answer I came up with this solution
val mainNavView = requireActivity().findViewById<View>(R.id.mainNavFragment)
Navigation.findNavController(mainNavView).navigate(R.id.action_next)
You can do it like this ;
in Child Fragment
// parentFragment = getParentFragment() for java
(parentFragment as MyParentFragment).myNavigationHandler(myArguments)
in Parent Fragment
fun myNavigationHandler(myArguments) {
Navigation.findNavController(binding.root)
.navigate(MyFragmentDirections.actionMyAction(myArguments))
}
findNavController().navigate(HostFragmentDirections.actionHostToOtherFragment())
HostFragment is just an example, which should be the name of host of your nested fragment.
For everyone struggling with navigating to somewhere from your nested fragment, this works.

Categories

Resources