Difference between revisions of "An Overview of the Android Jetpack Navigation Architecture Component"

From Techotopia
Jump to: navigation, search
(Passing Arguments)
Line 162: Line 162:
 
The main drawback to this particular approach is that it is not “type safe”. In other words, if the receiving destination treats an argument as being a different type than it was declared (for example treating a string as an integer) this error will not be caught by the compiler and will likely cause problems at runtime.
 
The main drawback to this particular approach is that it is not “type safe”. In other words, if the receiving destination treats an argument as being a different type than it was declared (for example treating a string as an integer) this error will not be caught by the compiler and will likely cause problems at runtime.
  
A better option, and the one used in this book is to make use of safeargs. Safeargs is an plugin for the Android Studio Gradle build system which automatically generates special classes that allow arguments to be passed in a type safe way. The safeargs approach to argument passing will be described and demonstrated in the next chapter ([[An Android Jetpack Navigation Component Tutorial]]).
+
A better option, and the one used in this book is to make use of safeargs. Safeargs is an plugin for the Android Studio Gradle build system which automatically generates special classes that allow arguments to be passed in a type safe way. The safeargs approach to argument passing will be described and demonstrated in the next chapter ([[An Android Navigation Component Tutorial]]).
  
 
== Summary ==
 
== Summary ==

Revision as of 13:34, 20 June 2018

Very few Android apps today consist of just a single screen. In reality, most apps comprise multiple screens through which the user navigates using screen gestures, button clicks and menu selections. Prior to the introduction of Android Jetpack, the implementation of navigation within an app was largely a manual coding process with no easy way to view and organize potentially complex navigation paths. This situation has improved considerably, however, with the introduction of the Android Navigation Architecture Component combined with support for navigation graphs in Android Studio.


Contents


Understanding Navigation

Every app has a home screen that appears after the app has launched and after any splash screen has appeared (a splash screen being the app branding screen that appears temporarily while the app loads). From this home screen, the user will typically perform tasks that will result in other screens appearing. These screens will usually take the form of other activities and fragments within the app. A messaging app, for example, might have a home screen listing current messages from which the user can navigate to either another screen to access a contact list or to a settings screen. The contacts list screen, in turn, might allow the user to navigate to other screens where new users can be added or existing contacts updated. Graphically, the app’s navigation graph might be represented as shown in Figure 1-1:

Jetpack navigation graph diagram.png 

Each screen that makes up an app, including the home screen, is referred to as a destination and is usually a fragment or activity. The Android navigation architecture uses a navigation stack to track the user’s path through the destinations within the app. When the app first launches, the home screen is the first destination placed onto the stack and becomes the current destination. When the user navigates to another destination, that screen becomes the current destination and is pushed onto the stack above the home destination. As the user navigates to other screens, they are also pushed onto the stack. Figure 1-2, for example, shows the current state of the navigation stack for the hypothetical messaging app after the user has launched the app and is navigating to the “Add Contact” screen:

Jetpack navigation stack diagram.png 

As the user navigates back through the screens using the system back button, each destination is popped off the stack until the home screen is once again the only destination on the stack. In Figure 1-3, the user has navigated back from the Add Contact screen, popping it off the stack and making the Contact List screen the current destination:

Jetpack navigation stack pop diagram.png 

All of the work involved in navigating between destinations and managing the navigation stack is handled by a navigation controller which is represented by the NavController class.

Adding navigation to an Android project using the Navigation Architecture Component is a straightforward process involving a navigation host, navigation graph, navigation actions and a minimal amount of code writing to obtain a reference to, and interact with, the navigation controller instance.

Declaring a Navigation Host

A navigation host is simply a special fragment (NavHostFragment) that is embedded into the user interface layout of an activity and serves as a placeholder for the destinations through which the user will navigate. Figure 1-4, for example, shows a typical activity screen and highlights the area represented by the navigation host fragment:

Jetpack navigation navhost.png 

A NavHostFragment can be placed into an activity layout within the Android Studio layout editor either by dragging and dropping an instance from the Containers section of the palette, or by manually editing the XML as follows:

<?xml version=”1.0” encoding=”utf-8”?>
<FrameLayout 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/container”
    android:layout_width=”match_parent”
    android:layout_height=”match_parent”
    tools:context=”.MainActivity” >

    <fragment
        android:id=”@+id/demo_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/navigation_graph” />
</FrameLayout>

The points of note in the above navigation host fragment element are the reference to the NavHostFragment in the name property, the setting of defaultNavHost to true and the assignment of the file containing the navigation graph to the navGraph property.

When the activity launches, this navigation host fragment is replaced by the home destination designated in the navigation graph. As the user navigates through the app screens, the host fragment will be replaced by the appropriate fragment for the current destination.


The Navigation Graph

A navigation graph is an XML file which contains the destinations that will be included in the app navigation. In addition to these destinations, the file also contains navigation actions that define navigation between destinations, and optional arguments for passing data from one destination to another. Android Studio includes a navigation graph editor that can be used to design graphs and implement actions either visually or by manually editing the XML.

Figure 1-5, shows the Android Studio navigation graph editor in Design mode:

Jetpack navigation editor.png 

The destinations list (A) provides a list of all of the destinations currently contained within the graph. Selecting a destination from the list will locate and select the corresponding destination in the graph (particularly useful for locating specific destinations in a large graph). The navigation graph panel (B) contains a dialog for each destination showing a representation of the user interface layout. In this example, this graph contains two destinations named mainFragment and secondFragment. Arrows between destinations (C) represent navigation action connections. Actions are added by hovering the mouse pointer over the edge of the origin until a circle appears, then clicking and dragging from the circle to the destination. The Attributes panel (D) allows the properties of the currently selected destination or action connection to be viewed and modified. In the above figure, the attributes for the action are displayed. New destinations are added by clicking on the button marked E and selecting options from a menu. Options are available to add existing fragments or activities as destinations, or to create new blank fragment destinations.

The underlying XML for the navigation graph can be viewed and modified by switching the editor into Text mode. The following XML listing represents the navigation graph for the destinations and action connection shown in Figure 1-5 above:

<?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/navigation_graph"
    app:startDestination="@id/mainFragment">

    <fragment
        android:id="@+id/mainFragment"
        android:name="com.ebookfrenzy.navigationdemo.ui.main.MainFragment"
        android:label="main_fragment"
        tools:layout="@layout/main_fragment" >
        <action
            android:id="@+id/mainToSecond"
            app:destination="@id/secondFragment" />
    </fragment>
    <fragment
        android:id="@+id/secondFragment"
        android:name="com.ebookfrenzy.navigationdemo.SecondFragment"
        android:label="fragment_second"
        tools:layout="@layout/fragment_second" >
    </fragment>
</navigation>

If necessary, navigation graphs can also be split over multiple files to improve organization and promote reuse. When structured in this way, nested graphs are embedded into root graphs. To create a nested graph, simply shift-click on the destinations to be nested, right-click over the first destination and select the Move to Nested Graph -> New Graph menu option. The nested graph will then appear as a new node in the graph. To access the nested graph, simply double-click on the nested graph node to load the graph file into the editor.

Accessing the Navigation Controller

Navigating from one destination to another will usually take place in response to an event of some kind within an app such as a button click or menu selection. Before a navigation action can be triggered, the code must first obtain a reference to the navigation controller instance. This requires a call to the findNavController() method of the Navigation or NavHostFragment classes. The following code, for example, can be used to access the navigation controller of an activity. Note that for the code to work, the activity must contain a navigation host fragment:

NavController controller = 
         Navigation.findNavController(activity, R.id.demo_nav_host_fragment);

In this case, the method call is passed a reference to the activity and the id of the NavHostFragment embedded in the activity’s layout.

Alternatively, the navigation controller associated with any view may be identified simply by passing that view to the method:

Button button = getView().findViewById(R.id.button);
NavController controller = Navigation.findNavController(button);

The final option finds the navigation controller for a fragment by calling the findNavController() method of the NavHostFragment class, passing through a reference to the fragment:

NavController controller = NavHostFragment.findNavController(fragment);

Triggering a Navigation Action

Once the navigation controller has been found, a navigation action is triggered by calling the controller’s navigate() method and passing through the resource id of the action to be performed. For example:

controller.navigate(R.id.goToContactsList);

The id of the action is defined within the Attributes panel of the navigation graph editor when an action connection is selected.

Passing Arguments

Data may be passed from one destination to another during a navigation action by making use of arguments which are declared within the navigation graph file. An argument consists of a name, type and an optional default value and may be added manually within the XML or using the Attributes panel when an action arrow or destination is selected within the graph. In Figure 1-6, for example, an integer argument named contactsCount has been declared with a default value of 0:

Jetpack navigation argument.png 

Once added, arguments are placed within the XML element if the receiving destination, for example:

<fragment
    android:id="@+id/secondFragment"
    android:name="com.ebookfrenzy.navigationdemo.SecondFragment"
    android:label="fragment_second"
    tools:layout="@layout/fragment_second" >
    <argument
        android:name="contactsCount"
        android:defaultValue=0
        app:type="integer" />
</fragment>

The Navigation Architecture Component provides two techniques for passing data between destinations. One approach involves placing the data into a Bundle object that is passed to the destination during an action where it is then unbundled and the arguments extracted.

The main drawback to this particular approach is that it is not “type safe”. In other words, if the receiving destination treats an argument as being a different type than it was declared (for example treating a string as an integer) this error will not be caught by the compiler and will likely cause problems at runtime.

A better option, and the one used in this book is to make use of safeargs. Safeargs is an plugin for the Android Studio Gradle build system which automatically generates special classes that allow arguments to be passed in a type safe way. The safeargs approach to argument passing will be described and demonstrated in the next chapter (An Android Navigation Component Tutorial).

Summary

The term Navigation within the context of an Android app user interface refers to the ability for a user to move back and forth between different screens. Once a time consuming to implement and difficult to organize, Android Studio and the Navigation Architecture Component now make it easier to implement and manage navigation within Android app project.

The different screens within an app are referred to as destinations and are usually represented by fragments or activities. All apps have a home destination which includes the screen displayed when the app first loads. The content area of this layout is replaced by a navigation host fragment which is swapped out for other destination fragments as the user navigates the app. The navigation path is defined by the navigation graph file consisting of destinations and the actions that connect them together with any arguments to be passed between destinations. Navigation is handled by navigation controllers which, in addition to manging the navigation stack, provide methods to initiate navigation actions from within app code.