A Useful Navigation Extension
As I was using a navigation component in my current project, I wanted to check it out, and it turned out to be a great decision. Google provided us with a very nice extension function. This function allows us to attach multiple navigation graphs to the BottomNavigationView. Let’s jump into implementation.
Here’s a link to the extension function that will be used in this example.
Creating menu and navigation graphs
“navigation_menu.xml”:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/home"
android:contentDescription="@string/navigation_menu_home"
android:icon="@drawable/ic_home"
android:title="@string/navigation_menu_home" />
<item
android:id="@+id/friends"
android:contentDescription="@string/navigation_menu_friends"
android:icon="@drawable/ic_friends"
android:title="@string/navigation_menu_friends" />
<item
android:id="@+id/messeges"
android:contentDescription="@string/navigation_menu_messeges"
android:icon="@drawable/ic_messeges"
android:title="@string/navigation_menu_messeges" />
</menu>
While creating a menu that will be attached to our BottomNavigationView keep in mind that the IDs for menu items will be the same as the IDs of our navigation graphs. Everything else can be named anything you desire.
“home.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"
app:startDestination="@+id/homeFragment">
<fragment
android:id="@+id/homeFragment"
android:name="com.mariusz.karwowski.sample.home.HomeFragment"
android:label="HOME"
tools:layout="@layout/fragment_home">
<action
android:id="@+id/homeToPlayer"
app:destination="@+id/playerFragment"/>
</fragment>
<fragment
android:id="@+id/playerFragment"
android:name="com.mariusz.karwowski.sample.home.PlayerFragment"
android:label="PLAYER"
tools:layout="@layout/fragment_player">
<argument
android:name="movieUrl"
app:argType="string" />
</fragment>
</navigation>
We created a sample navigation graph for our home path. As mentioned before, we named our XML file “home.xml” and the navigation graph’s ID is “home”, which will allow the navigation component to easily chain menu items with the proper navigation graph.
The headings of the remaining graphs should look like this:
“friends.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/friends"
app:startDestination="@+id/friendsFragment">
...
</navigation>
“messages.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/messeges"
app:startDestination="@+id/messegesFragment">
...
</navigation>
We have our menu and we have our navigation graphs - we now need a BottomNavigationView.
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/black"
app:itemBackground="@color/transparent"
app:itemIconTint="@drawable/ic_home_selector"
app:itemTextColor="@drawable/ic_home_selector"
app:labelVisibilityMode="labeled"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/navHostFragment"
app:layout_insetEdge="bottom"
app:menu="@menu/navigation_menu" />
You can notice a couple differences compared to Google’s introduction to the navigation component: there is no android:name="androidx.navigation.fragment.NavHostFragment" or app:defaultNavHost="true" app:navGraph="@navigation/nav_graph", because the function linked above will handle this for us. All we have to do is to link the menu created before.
Final setup in activity
There are quite a few things that we have to do in the activity that will be hosting the BottomNavigationView:
Declare NavController LiveData that will be used for back presses:
private var currentNavController: LiveData<NavController>? = null
Override onSupportNavigateUp using LiveData:
override fun onSupportNavigateUp() =
currentNavController?.value?.navigateUp() ?: false
Declare a list of our navigation graphs:
val navGraphIds = listOf(R.navigation.home, R.navigation.friends, R.navigation.messeges)
We can now use the provided extension function to set up the BottomNavigationView:
val controller = bottomNavigationView.setupWithNavController(
navGraphIds = navGraphIds,
fragmentManager = supportFragmentManager,
containerId = R.id.navHostFragment,
intent = intent
currentNavController = controller
The function parameters are: the list of navigation graphs, the fragment manager, and the ID of the FrameLayout that will be used as a container for the fragments and intent from activity. Don’t forget to assign the generated LiveData to our property.
What I like to do is remove the optional function that will pop all fragments on reselection of the menu item:
setupItemReselected(graphIdToTagMap, fragmentManager)
You can find this in the 135th line of the provided extension function.
Tips
Here are some useful tips for working with multiple navigation graphs:
One destination in multiple navigation graphs
If you want to use one fragment in more than one navigation graph, all you have to do is to create a new navigation graph for this fragment:“player.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/player"
app:startDestination="@+id/playerFragment">
<fragment
android:id="@+id/playerFragment"
android:name="com.mariusz.karwowski.sample.home.PlayerFragment"
android:label="PLAYER"
tools:layout="@layout/fragment_player">
</fragment>
</navigation>
And now you have to include this navigation graph in all graphs which need to have this fragment in their navigation paths, but remember to pass the navigation graph IDs as destinations instead of fragment IDs.
<?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"
app:startDestination="@+id/homeFragment">
<fragment
android:id="@+id/homeFragment"
android:name="com.mariusz.karwowski.sample.home.HomeFragment"
android:label="HOME"
tools:layout="@layout/fragment_home">
<action
android:id="@+id/homeToPlayer"
app:destination="@+id/player"/>
</fragment>
<include app:graph="@navigation/player" />
</navigation>
Arguments for starting destinations
If your newly created fragment still has to have arguments and you want to pass them with directions, you can’t add arguments to fragments which are starting destinations of the graph; instead, you need to add arguments to actions, so in the above the home fragment with all its new actions will look like this
<fragment
android:id="@+id/homeFragment"
android:name="com.mariusz.karwowski.sample.home.HomeFragment"
android:label="HOME"
tools:layout="@layout/fragment_home">
<action
android:id="@+id/homeToPlayer"
app:destination="@+id/player">
<argument
android:name="movieUrl"
app:argType="string" />
</action>
</fragment>
In addition, you will be required to fetch all these arguments by yourself from the PlayerFragment.
Summary
The sample provided by Google allows us to easily handle complex fragment management which would otherwise have to be implemented programmatically inside menu option selection handling. We can see the application flow presented in nice graph previews. We can create deep links to our fragments and the whole navigation graph should be recreated, so we do not need to create fragment stacks by ourselves. What’s more, our destinations can have our custom classes as arguments - just remember to extend Serializable or Parcelable. Overall, I would suggest you create your own sample project that uses Navigation Components so you can see for yourself how easy it is.