I am building an app that uses BottomNavigationView and Navigation Architecture Component (https://github.com/fkshiba/POCNavigation).
I have one graph.xml for each tab and the transition between tabs is done by the Activity without Navigation AC due to the multiple backstack issue. I have an action with a transition animation on home_graph.xml from Home2 to Home3.
The problem is, once I run this transition and pop back to Home2 then navigate to another tab. It runs the pop animation again for the Home2 fragment when I transition from another tab even though there wasn't any animation specified for it.
Does anyone know a solution for this issue?
I see your code on GitHub and i assume that you are using android navigation wrongly. First you don't need to create different graphs(in your case four) for
different fragments. Just create one graph and add all fragments in it that you have to use within your bottom navigation and for others transactions.
And secondly you don't need to perform fragment transactions manually (in your activity code) you can but not necessary.
Your updated code:
only one graph:
<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_graph"
app:startDestination="#id/home2Fragment">
<fragment
android:id="#+id/home2Fragment"
android:name="com.felipeshiba.pocnavigation.Home2Fragment"
android:label="fragment_home2"
tools:layout="#layout/fragment_home2" >
<action
android:id="#+id/action_home2Fragment_to_home3Fragment"
app:destination="#id/home3Fragment"
app:enterAnim="#anim/nav_default_enter_anim"
app:exitAnim="#anim/nav_default_exit_anim"
app:popEnterAnim="#anim/nav_default_pop_enter_anim"
app:popExitAnim="#anim/nav_default_pop_exit_anim"/>
</fragment>
<fragment
android:id="#+id/home3Fragment"
android:name="com.felipeshiba.pocnavigation.Home3Fragment"
android:label="fragment_home3"
tools:layout="#layout/fragment_home3" >
<deepLink
android:id="#+id/deepLink"
app:uri="ifood://home/" />
</fragment>
<fragment
android:id="#+id/orders2Fragment"
android:name="com.felipeshiba.pocnavigation.Orders2Fragment"
android:label="fragment_orders2"
tools:layout="#layout/fragment_orders2" />
<fragment
android:id="#+id/profile2Fragment"
android:name="com.felipeshiba.pocnavigation.Profile2Fragment"
android:label="fragment_profile2"
tools:layout="#layout/fragment_profile2" />
<fragment
android:id="#+id/search2Fragment"
android:name="com.felipeshiba.pocnavigation.Search2Fragment"
android:label="fragment_search2"
tools:layout="#layout/fragment_search2" /></navigation>
Your updated activity code:
class NavigationActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_navigation)
val host: NavHostFragment = supportFragmentManager
.findFragmentById(R.id.navigation_container) as NavHostFragment? ?: return
val navController = host.navController
bottom_navigation.setupWithNavController(navController)
}}
Now you can just call your botton navigation items(fragments) within your menu by replacing menu item id with fragments id like below:
updated menu.xml:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/home2Fragment"
android:icon="?android:attr/actionModeCopyDrawable"
android:title="Home" />
<item
android:id="#+id/search2Fragment"
android:icon="#android:drawable/ic_menu_search"
android:title="Search" />
<item
android:id="#+id/orders2Fragment"
android:icon="?android:attr/actionModePasteDrawable"
android:title="Orders" />
<item
android:id="#+id/profile2Fragment"
android:icon="?android:attr/actionModeCutDrawable"
android:title="Profile" /></menu>
Your activity.xml:
<android.support.constraint.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".NavigationActivity">
<fragment
android:id="#+id/navigation_container"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:visibility="gone"
app:defaultNavHost="true"
app:navGraph="#navigation/home_graph"
app:layout_constraintBottom_toTopOf="#id/bottom_navigation"
app:layout_constraintTop_toTopOf="parent" />
<android.support.design.widget.BottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:menu="#menu/bottom_navigation"></android.support.constraint.ConstraintLayout>
For other updated(working) code like manifest check here
NavigationUI will always pop to start destination before going to other tab in BottomNavigationView, it's just how the nav lib works. Set custom OnNavigationItemSelectedListener and navigate yourself, and pass your animations through NavOptions.
Related
Having passed through multiple solutions on this site, while being new to navigation Fragments and Android in general I have this issue:
I had 4 fragments that worked well together with the bottom navbar making the navigation;
I added a favourite slides button on HomeFragment that leads to a FavouriteSlides Fragment that works;
From the FavouriteSlides I still can access all 3 buttons, except Home. When I click the Home icon nothing happens. ( wrong)
From all the other Fragments I cannot access Home Fragment because it is replaced by Favourite Slides Fragment ( wrong)
BackArrow works Ok and all the Sliders are pointing ok to Home Fragment on Back Button pressed.
mobile_navigation.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/mobile_navigation"
app:startDestination="#id/navigation_home">
<fragment
android:id="#+id/navigation_home"
android:name="com.example.spiritualvietnam.ui.fragments.HomeFragment"
android:label="HomeFragment"
tools:layout="#layout/home">
<action
android:id="#+id/action_navigation_home_to_favouritesFragment"
app:destination="#id/favouritesFragment" />
</fragment>
<fragment
android:id="#+id/navigation_places"
android:name="com.example.spiritualvietnam.ui.fragments.PlacesFragment"
android:label="PlacesFragment"
tools:layout="#layout/fragment_places_recyclerview">
<action
android:id="#+id/action_navigation_places_to_navigation_home"
app:destination="#id/navigation_home" />
</fragment>
<fragment
android:id="#+id/navigation_settings"
android:name="com.example.spiritualvietnam.ui.fragments.SettingsFragment"
android:label="SettingsFragment"
tools:layout="#layout/fragment_settings">
<action
android:id="#+id/action_navigation_settings_to_navigation_home"
app:destination="#id/navigation_home" />
</fragment>
<fragment
android:id="#+id/navigation_sliders"
android:name="com.example.spiritualvietnam.ui.fragments.SlidingPhotosFragment"
android:label="SlidingPhotosFragment"
tools:layout="#layout/fragment_sliding_photos">
<action
android:id="#+id/action_navigation_sliders_to_navigation_home"
app:destination="#id/navigation_home" />
</fragment>
<fragment
android:id="#+id/favouritesFragment"
android:name="com.example.spiritualvietnam.ui.fragments.FavouritesFragment"
android:label="FavouritesFragment" >
<action
android:id="#+id/action_favouritesFragment_to_navigation_home"
app:destination="#id/navigation_home" />
</fragment>
</navigation>
bottom_nav_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/navigation_home"
android:icon="#drawable/ic_home_icon"
android:title="#string/home" />
<item
android:id="#+id/navigation_sliders"
android:icon="#drawable/ic_lampion_play"
android:title="#string/sliders" />
<item
android:id="#+id/navigation_places"
android:icon="#drawable/ic_baseline_place_24"
android:title="#string/places" />
<item
android:id="#+id/navigation_settings"
android:icon="#drawable/ic_baseline_add_alert_24"
android:title="#string/add_alerts" />
</menu>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="#drawable/gradient4navbar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="#menu/bottom_nav_menu" />
<fragment
android:id="#+id/nav_host_fragment_activity_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Main Activity
class MainActivity : AppCompatActivity() {
// Binding nu uita - baga click pe layout la becul galben si zi sa adauge data binding k omu
private lateinit var binding: ActivityMainBinding
lateinit var tinyDBMain: TinyDB
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestWindowFeature(Window.FEATURE_NO_TITLE)
this.window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
supportActionBar?.hide() // hides title and stuff
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val navView: BottomNavigationView = binding.navView
val navController = findNavController(R.id.nav_host_fragment_activity_main)
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.navigation_home, R.id.navigation_sliders, R.id.navigation_places, R.id.navigation_settings,
)
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
// works here for all the fragments
tinyDBMain = TinyDB(this)
}
It's ok to answer in Java as well if you know the answer.
If I should include more code I will I wanted to keep it as short as I could.
Thank you
Your appBarConfiguration and your menu items might be the reason why its not working you need to have your favoritesFragment id there, like this:
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.navigation_home,
R.id.navigation_sliders,
R.id.navigation_places,
R.id.favouritesFragment
)
)
and like this:
<item
android:id="#+id/favouritesFragment"
android:icon="#drawable/ic_baseline_add_alert_24"
android:title="#string/add_alerts" />
Generally the id from the fragments inside the navigation graph and inside the menu items has to be the same so that it works properly.
You also don't need to have actions inside your navigation graph, since the library, when usign a bottom navigaiton will automatically create the navigation, by using this configuration.
Let me know if this worked for you.
I have found out that making the navbar disappear in the FavouritesFragment (that hasn't got an Icon in the navbar) is the best solution.
I implemented in Main Activity onCreate() the custom navbar behaviour for all the Fragments.
The answer that indirectly helped me, after days of searching, is here: how to hide BottomNavigationView on android-navigation lib
Starting with version 2.4.0-alpha01, the NavigationUI helpers support multiple back stacks without any code change. If your app uses the setupWithNavController() methods for BottomNavigationView or NavigationView, all you need to do is to update the dependencies and multiple back stack support will be enabled by default.
In my aplication I have "Intro" screen.(TestFragment)
When user clicks on the button, app is navigating to home screen where Bottom navigation is present.
When navigating from "Intro screen" I want to clear back stack.
<fragment
android:id="#+id/testFragment"
android:name="com.example.android.navigationadvancedsample.TestFragment"
android:label="Test" >
<action
app:popUpToInclusive="true"
app:popUpTo="#id/testFragment"
android:id="#+id/action_test_to_title"
app:destination="#id/titleScreen" />
</fragment>
In this situation multiple back stack funcionality is not working. There is just one back stack.
However when property popUpToInclusive is set to false multiple back stack are present and everything is working as expected!
bottom_nav.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/titleScreen"
android:icon="#drawable/ic_home"
android:contentDescription="#string/cd_home"
android:title="#string/title_home" />
<item
android:id="#+id/leaderboard"
android:icon="#drawable/ic_list"
android:contentDescription="#string/cd_list"
android:title="#string/title_list" />
<item
android:id="#+id/register"
android:icon="#drawable/ic_feedback"
android:contentDescription="#string/cd_form"
android:title="#string/title_register" />
</menu>
activity_main.xml
<LinearLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.android.navigationadvancedsample.MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/nav_host_container"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottom_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="#menu/bottom_nav"/>
</LinearLayout>
MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navHostFragment = supportFragmentManager.findFragmentById(
R.id.nav_host_container
) as NavHostFragment
navController = navHostFragment.navController
// Setup the bottom navigation view with navController
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottom_nav)
bottomNavigationView.setupWithNavController(navController)
// Setup the ActionBar with navController and 3 top level destinations
appBarConfiguration = AppBarConfiguration(
setOf(R.id.titleScreen, R.id.leaderboard, R.id.register)
)
setupActionBarWithNavController(navController, appBarConfiguration)
}
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/testFragment">
<fragment
android:id="#+id/titleScreen"
android:name="com.example.android.navigationadvancedsample.homescreen.Title"
android:label="#string/title_home">
<action
android:id="#+id/action_title_to_about"
app:destination="#id/aboutScreen"/>
</fragment>
<fragment
android:id="#+id/aboutScreen"
android:name="com.example.android.navigationadvancedsample.homescreen.About"
android:label="#string/title_about" />
<fragment
android:id="#+id/leaderboard"
android:name="com.example.android.navigationadvancedsample.listscreen.Leaderboard"
android:label="#string/title_list">
<action
android:id="#+id/action_leaderboard_to_userProfile"
app:destination="#id/userProfile"/>
</fragment>
<fragment
android:id="#+id/userProfile"
android:name="com.example.android.navigationadvancedsample.listscreen.UserProfile"
android:label="#string/title_detail">
<deepLink
android:id="#+id/deepLink"
app:uri="www.example.com/user/{userName}"
android:autoVerify="true"/>
<argument
android:name="userName"
app:argType="string"/>
</fragment>
<fragment
android:id="#+id/register"
android:name="com.example.android.navigationadvancedsample.formscreen.Register"
android:label="#string/title_register">
<action
android:id="#+id/action_register_to_registered"
app:destination="#id/registered"/>
</fragment>
<fragment
android:id="#+id/registered"
android:name="com.example.android.navigationadvancedsample.formscreen.Registered"
android:label="Registered" />
<fragment
android:id="#+id/testFragment"
android:name="com.example.android.navigationadvancedsample.TestFragment"
android:label="Test" >
<action
app:popUpToInclusive="false"
app:popUpTo="#id/testFragment"
android:id="#+id/action_test_to_title"
app:destination="#id/titleScreen" />
</fragment>
</navigation>
Why multiple back stack are not supported when navigating from another fragment and clearing back stack? Maybe this is a bug in the jetpack navigation library.
I recently walkthrough to your code and found that in your nav_graph.xml you defined testFragment as startDestination, this is the start point of your application, so when you are on welcome screen that you have bottomNavigationView, when you click on the back button, it will return to testFragment Screen, if your app is single activity (single page application)
you have to implement OnBackPressedCallback on the first fragment of your welcome screen, some sample code:
OnBackPressedCallback callback = new OnBackPressedCallback(true) {
#Override
public void handleOnBackPressed() {
NavBackStackEntry backStackEntry =
getNavController().getPreviousBackStackEntry();
if (backStackEntry != null) {
NavDestination destination = backStackEntry.getDestination();
if (destination != null) {
if (destination.getId() == R.id.homeFragment) {
requireActivity().finish();
}
}
}
}
};
requireActivity().getOnBackPressedDispatcher().addCallback(callback);
How do I set up navigation component with navigation drawer?
How do I use it in my app?
Can everything be done with one Activity?
How do I handle toolbar visibility with just one Activity and fragments which have a dynamic toolbar visibility. Also, there are fragments which I need to close the drawer and make it inaccessible.
This question is a self answered question and works more as a tutorial than a real-life QA.
How do I set up navigation component with navigation drawer?
How do I use it in my app?
The navigation drawer set up differs a little when it comes to Navigation component.
Note, if you create a new app with drawer navigation, the current tutorial is not needed. However I'm going to explain some things that might look strange around here and if you decide to add a drawer at a later stage of the app
First, you need to set up your activity_main.xml and MainActivity to be ready for the Navigation Architecture:
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout 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/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<include
layout="#layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.material.navigation.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="#layout/nav_header_main"
app:menu="#menu/activity_main_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>
where app_bar_main is just:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<include layout="#layout/content_main" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
And the content_main is where your fragments are going to be held:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:showIn="#layout/app_bar_main">
<fragment
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>
Things you should know: The activity mustn't have a AppBar set on AndroidManifest.xml:
android:theme="#style/AppTheme.NoActionBar"
If you notice app:menu="#menu/activity_main_drawer" in the NavigationView tag, the fragment names should be the same that they are inside the mobile_navigation.xml:
<group android:checkableBehavior="single">
<item
android:id="#+id/homeFragment"
android:icon="#drawable/ic_menu_camera"
android:title="#string/menu_home" />
<item
android:id="#+id/galleryFragment"
android:icon="#drawable/ic_menu_gallery"
android:title="#string/menu_gallery" />
<item
android:id="#+id/slideshowFragment"
android:icon="#drawable/ic_menu_slideshow"
android:title="#string/menu_slideshow" />
<item
android:id="#+id/toolsFragment"
android:icon="#drawable/ic_menu_manage"
android:title="#string/menu_tools" />
</group>
<item android:title="Communicate">
<menu>
<item
android:id="#+id/shareFragment"
android:icon="#drawable/ic_menu_share"
android:title="#string/menu_share" />
<item
android:id="#+id/sendFragment"
android:icon="#drawable/ic_menu_send"
android:title="#string/menu_send" />
</menu>
</item>
</menu>
This way, with what is going to be explained below there will be no need to call for onCreateOptionsMenu to detect the clicks. Android team has already solved that for us. Follow below.
Until now, this doesn't differ too much from the usual drawer set up we actually do. But, there are some configurations we would need to do in the logic part of the app. So let's open MainActivity.kt. First you will need these:
private var appBarConfiguration: AppBarConfiguration? = null
private var drawerLayout: DrawerLayout? = null
private var toolbar: Toolbar? = null
private var navController: NavController? = null
After that in your onCreate method:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar) //set the toolbar
drawerLayout = findViewById(R.id.drawer_layout)
val navView: NavigationView = findViewById(R.id.nav_view)
navController = findNavController(R.id.nav_host_fragment)
appBarConfiguration = AppBarConfiguration(
setOf(
R.id.homeFragment,
R.id.galleryFragment,
R.id.slideShowFragment,
R.id.toolsFragment,
R.id.shareFragment,
R.id.sendFragment,
R.id.loginFragment,
R.id.phoneConfirmationFragment
), drawerLayout
)
setupActionBarWithNavController(navController!!, appBarConfiguration!!) //the most important part
navView.setupWithNavController(navController!!) //the second most important part
//other things unrelated
}
Let's see what's going on here:
First you would need a reference to the navController. The AppBarConfiguration is just a class which holds the fragments that are going to be opened as top level destinations. Which means that the fragment which is going to be opened after them would release the current one from the fragment back stack. It's important to tell to the AppBarConfiguration that we have a drawer also (passed as a parameter in the constructor).
Down below you would have a method called onSupportNavigateUp():
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
return navController.navigateUp(appBarConfiguration!!) || super.onSupportNavigateUp()
}
This method has to do with the up back button. But you won't need it too much if you have drawer navigation. This really comes in handy when you have a lot of fragments added on the backstack (or at least two).
Can everything be done with one Activity?
Yes, definitely! but still it requires a little bit more work when it comes to conditional navigation. Like when you want to show fragments which are not part of your drawer app. But still Google has done a huge progress with it. You can refer to conditional navigation here.
How do I handle toolbar visibility with just one Activity and
fragments which have a dynamic toolbar visibility. Also, there are
fragments which I need to close the drawer and make it inaccessible.
You can use addOnDestinationChangedListener from the navController:
navController.addOnDestinationChangedListener { _, destination, _ ->
when (destination.id) {
R.id.loginFragment, R.id.registerFragment, R.id.phoneConfirmationFragment -> {
toolbar?.visibility = View.GONE
drawerLayout?.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
}
else -> {
toolbar?.visibility = View.VISIBLE
drawerLayout?.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
}
}
}
Now you have a drawer and navigation component on your app.
I used BottomNavigationView along with NavHostFragment and I have only 2 tabs at the moment, down below the Navigation Graph I used
<?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/main_navigation"
app:startDestination="#id/fragment1">
<fragment
android:id="#+id/fragment1"
android:name="something.ListingFragment"
android:label="#string/hello" />
<fragment
android:id="#+id/fragment2"
android:name="something.ListingFragment2"
android:label="#string/Hi" />
</navigation>
And the BottomNavigation Menu is:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<item
android:id="#id/fragment1"
android:icon="#drawable/ic1"
android:title="#string/hello"
tools:showAsAction="ifRoom" />
<item
android:id="#id/fragment2"
android:icon="#drawable/ic2"
android:title="#string/hi"
tools:showAsAction="ifRoom" />
All setuped together using:
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottom_navigation_view)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.navigation_host_fragment) as NavHostFragment
NavigationUI.setupWithNavController(bottomNavigationView, navHostFragment.navController)
Question is, every time I switch between tabs it creates a new instance of the tab fragment I click on, is there a way to manage this to prevent the creation of new items if it already exists in the memory?
I am using the Android Navigation Architecture Components for navigating a Fragment-based-, Single-Activity-application. The main navigation is implemented via NavigationDrawer-pattern: DrawerLayout with NavigationView and NavHostFragment plus and additional Toolbar.
Everything is working fine so far - I am able to navigate to every Fragment -, except for the BackStack of the main-level Fragments. Any Fragment other than the startDestination seems to be added to the BackStack instead of being replaced like a regular NavigationDrawer would do.
The misbehavior can be identified via the Burger-icon that switches to a Back-icon as soon as I navigate to a Fragment other than the startDestination, and by navigating back which leads me to the startDestination instead of exiting the app.
How should the Navigation Architecture Components be used in order to replace the main Fragments in combination with a NavigationView?
I am initializing the Navigation Architecture Components like this:
class MainActivity : AppCompatActivity {
private val navigationHostFragment: NavHostFragment
get() = supportFragmentManager.findFragmentById(R.id.navigationHost) as NavHostFragment
private val navigationController: NavController
get() = navigationHostFragment.navController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity)
// Views are bound via Kotlin Android Extensions
setSupportActionBar(toolbar)
NavigationUI.setupWithNavController(toolbar, navigationController, drawerLayout)
NavigationUI.setupWithNavController(navigationView, navigationController)
}
override fun onSupportNavigateUp(): Boolean = findNavController(R.id.navigationHost).navigateUp()
}
The layout for the activity (/res/layout/activity.xml) looks like this:
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"/>
<fragment
android:id="#+id/navigationHost"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navGraph="#navigation/navigation"
app:defaultNavHost="true"/>
</LinearLayout>
<com.google.android.material.navigation.NavigationView
android:id="#+id/navigationView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:menu="#menu/navigation"/>
</androidx.drawerlayout.widget.DrawerLayout>
The navigation graph (/res/navigation/navigation.xml) looks like this:
<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/first">
<fragment
android:id="#+id/first"
android:name="de.example.FirstFragment"
android:label="#string/first"/>
<fragment
android:id="#+id/second"
android:name="de.example.SecondFragment"
android:label="#string/second"/>
</navigation>
The menu for the NavigationView (/res/menu/navigation.xml) looks like this:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
<item
android:id="#+id/first"
android:title="#string/first"/>
<item
android:id="#+id/second"
android:title="#string/second"/>
</group>
</menu>
The relevant dependencies look like this:
dependencies {
implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.core:core-ktx:1.0.0'
implementation 'android.arch.navigation:navigation-fragment-ktx:1.0.0-alpha06'
implementation 'android.arch.navigation:navigation-ui-ktx:1.0.0-alpha06'
}
As Alex posted, my original goal clashes with the Principles of Navigation:
Apps have a fixed destination which is the screen the user sees when they launch your app from the launcher. This destination should also be the last screen the user sees when they return to the launcher after pressing the back button.