Adding a Jetpack Navigation Graph in Android

Jetpack Navigation

Using dynamic and static fragments, although very flexible, introduces a lot of boilerplate code into your app and can become quite complicated when user journeys require adding, removing, and replacing multiple fragments while managing the back stack. Google introduced the Jetpack components. The Navigation component within the suite of Jetpack components enables you to reduce boilerplate code and simplify navigation within your app. We are going to use it now to update the Star Sign app to use this component.

Exercise: Adding a Jetpack Navigation Graph

In this exercise, we are going to reuse most of the classes and resources from the last exercise. We will first create an empty project and copy the resources. Next, we will add the dependencies and create a navigation graph. Using a step-by-step approach, we will configure the navigation graph and add destinations to navigate between fragments. Perform the following steps:

1. Create a new project with an Empty Activity called Jetpack Fragments.

2. Copy strings.xml, fragment_detail.xml, fragment_list.xml, DetailFragment, and ListFragment from the previous exercise, remembering to change the app_name string in strings.xml and the package name for the fragment classes. Finally, add the styles defined below the base application style in themes.xml from the last exercise to the themes.xml in this project. You will also need to add the constant property const val STAR_SIGN_ID = “STAR_SIGN_ID” above the class header in the MainActivity.

3. Once you have done that, add the following dependencies you need to use the Navigation component into app/build.gradle within the dependences{ } block:implementation “androidx.navigation:navigation-fragment-ktx:2.3.2″implementation “androidx.navigation:navigation-ui-ktx:2.3.2”

It will prompt you to sync now in the top-right hand corner of the screen to update the dependencies. Click the button, and after they’ve updated, make sure the ‘app’ module is selected and go to File | New | Android Resource file:

Figure 3.17: Menu option to create Android Resource File
Figure 3.17: Menu option to create Android Resource File

4. Once this dialog appears, change the Resource type to Navigation and then name the file nav_graph:

Figure 3.18: New Resource File dialog
Figure 3.18: New Resource File dialogClick OK to proceed. This creates a new folder in the res folder called Navigation with nav_graph.xml inside it.

5. Opening the file, you see the following code:

< ? 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"
android: id = "@+id/nav_graph" > < /navigation>

As it’s not being used anywhere, you might see a warning with the <navigation> element underlined in red:Figure 3.19: Navigation not used warning underline
Figure 3.19: Navigation not used warning underlineIgnore this for now.

6. Update the nav_graph.xml navigation file with the following code:

< ? 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/nav_graph"
app: startDestination = "@id/starSignList" > < fragment android: id = "@+id/starSignList"
android: name = "com.example.jetpackfragments.ListFragment"
android: label = "List"
tools: layout = "@layout/fragment_list" > < action android: id = "@+id/star_sign_id_action"
app: destination = "@id/starSign" > < /action>    </fragment > < fragment android: id = "@+id/starSign"
android: name = "com.example.jetpackfragments.DetailFragment"
android: label = "Detail"
tools: layout = "@layout/fragment_detail" / > < /navigation>

7. The preceding file is a working Navigation graph. Although the syntax is unfamiliar, it is quite straightforward to understand:a. The ListFragment and DetailFragment are present as they would be if you were adding static fragments.b. There is an id to identify the graph at the root <navigation> element and IDs on the fragments themselves. Navigation graphs introduce the concept of destinations, so at the root navigation level, there is the app:startDestination, which has the ID of starSignList, which is the ListFragment, then within the <fragment> tag, there is the <action> element.c. Actions are what link the destinations within the navigation graph together. The destination action here has an ID so you can refer to it in code and has a destination, which, when used, it will direct to. Now that you’ve added the navigation graph, you need to use it to link the activity and fragments together.

8. Open up activity_main.xml and replace the TextView inside the ConstraintLayout with the following FragmentContainerView:

< ? xml version = "1.0"
encoding = "utf-8" ? > < androidx.fragment.app.FragmentContainerView xmlns : android = "http://schemas.android.com/apk/res/android"
xmlns: app = "http://schemas.android.com/apk/res-auto"
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: navGraph = "@navigation/nav_graph" / >

FragmentContainerView has been added with name android:name=”androidx.navigation.fragment.NavHostFragment”. It will host the fragments from the app:navGraph=”@navigation/nav_graph” that you have just created. The app:defaultNavHost states that it is the app’s default navigation graph. It also controls the back navigation when one fragment replaces another. You can have more than one NavHostFragment in a layout for controlling two or more areas of the screen that manage their own fragments, which you might use for dual-pane layouts in tablets, but there can only be one default. There are a few changes you need to make to make the app work as expected in the ListFragment.

9. Firstly, remove the class file header and references to the StarSignListener. So, the following will be replaced:

interface StarSignListener {
  fun onSelected(starSignId: Int)
}
class ListFragment: Fragment(), View.OnClickListener {
    private lateinit
    var starSignListener: StarSignListener override fun onAttach(context: Context) {
      super.onAttach(context) if (context is StarSignListener) {
        starSignListener = context
      } else {
        throw RuntimeException("Must implement StarSignListener")
      }
    }

And it will be replaced with the following line of code:

class ListFragment : Fragment() {

10. Next, at the bottom of the class, remove the onClick overridden method as you are not implementing the View.OnClicklistener:

override fun onClick(v: View ? ) {
  v?.let {
    starSign -> starSignListener.onSelected(starSign.id)
  }
}

11. In the onViewCreated method, replace the forEach statement that loops over the star sign views:

starSigns.forEach {
  it.setOnClickListener(this)
}

Replace it with the following code below and add the Navigation import to the imports list:

import androidx.navigation.NavigationstarSigns.forEach {
  starSign -> val fragmentBundle = Bundle() fragmentBundle.putInt(STAR_SIGN_ID, starSign.id) starSign.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.star_sign_id_action, fragmentBundle))
}

Here, you are creating a bundle to pass the STAR_SIGN_ID with the view ID of the selected star sign to a NavigationClickListener. It uses the ID of the R.id.star_sign_id_action action to load the DetailFragment when clicked as that is the destination for the action. The DetailFragment does not need any changes and uses the passed-in fragment argument to load the details of the selected star sign ID.

12. Run up the app, and you’ll see that the app behaves as it did before.

Now you’ve been able to remove a significant amount of boilerplate code and documented the navigation within the app in the navigation graph. In addition, you have offloaded more of the management of the fragment lifecycle to the Android framework, saving more time to work on features. Jetpack Navigation is a powerful androidx component and enables you to map your whole app and the relationships between fragments, activities, and so on. You can also use it selectively to manage different areas of your app that have a defined user flow, such as the startup of your app and guiding the user through a series of welcome screens, or some wizard layout user journey, for example.

With this knowledge, let’s try completing an activity using the techniques learned from all these exercises.

Written by

XR Developer responsible for end-to-end development of XR solutions spanning multiple domains, by using various XR and WebXR libraries.

Leave a Reply