1. Welcome
In the previous codelab, you modified the AndroidTrivia app to add a Fragment to an existing activity. In this codelab, you add navigation to that app.
Introduction to navigation
Structuring the user's experience of navigating through an app has always been an interesting topic for developers. For Android apps, the Navigation Architecture Component makes it easier to implement navigation.
A destination is any place inside the app to which a user can navigate. A navigation graph for an app consists of a set of destinations within the app. Navigation graphs allow you to visually define and customize how users navigate among destinations in your app.
What you should already know
- The fundamentals of Kotlin
- How to create basic Android apps in Kotlin
- How to work with layouts
What you'll learn
- How to use navigation graphs
- How to define navigation paths in your app
- What an Up button is, and how to add one
- How to create an options menu
- How to create a navigation drawer
What you'll do
- Create a navigation graph for your fragments using the navigation library and the Navigation Editor.
- Create navigation paths in your app.
- Add navigation using the options menu.
- Implement an Up button so that the user can navigate back to the title screen from anywhere in the app.
- Add a navigation drawer menu.
2. App overview
The AndroidTrivia app, which you started working on in the previous codelab, is a game in which users answer questions about Android development. If the user answers three questions correctly, they win the game.
If you completed the previous codelab, use that code as the starter code for this codelab. Otherwise, download the AndroidTriviaFragment app from GitHub to get the starter code.
In this codelab, you update the AndroidTrivia app in the following ways:
- You create a navigation graph for the app.
- You add navigation for a title screen and a game screen.
- You connect the screens with an action, and you give the user a way to navigate to the game screen by tapping Play.
- You add an Up button, which is shown as the left-arrow at the top of some screens.
3. Task: Add navigation components to the project
Step 1: Add navigation dependencies
The Navigation component is a library that can manage complex navigation, transition animation, deep linking, and compile-time checked argument passing between the screens in your app.
To use the navigation library, you need to add the navigation dependencies to your Gradle files.
- To get started, download the AndroidTriviaFragment starter app or use your own copy of the AndroidTrivia app from the previous codelab. Open the AndroidTrivia app in Android Studio.
- In the Project: Android pane, open the Gradle Scripts folder. Double-click the project-level build.gradle file to open the file.
- At the top of the project-level
build.gradle
file, along with the otherext
variables, add a variable for thenavigationVersion
. To find the latest navigation version number, see Declaring dependencies in the Android developer documentation.
ext {
...
navigationVersion = "2.3.0"
...
}
- In the Gradle Scripts folder, open the module-level build.gradle file. Add the dependencies for
navigation-fragment-ktx
andnavigation-ui-ktx
, as shown below:
dependencies {
...
implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion"
implementation "androidx.navigation:navigation-ui-ktx:$navigationVersion"
...
}
- Rebuild the project.
Step 2: Add a navigation graph to the project
- In the Project: Android pane, right-click the res folder and select New > Android Resource File.
- In the New Resource File dialog, select Navigation as the Resource type.
- In the File name field, name the file
navigation
. - Make sure the Chosen qualifiers box is empty, and click OK. A new file, navigation.xml, appears in the res > navigation folder.
- Open the res > navigation > navigation.xml file and click the Design tab to open the Navigation Editor. Notice the No NavHostFragments found message in the layout editor. You fix this problem in the next task.
4. Task: Create the NavHostFragment
A navigation host fragment acts as a host for the fragments in a navigation graph. The navigation host Fragment is usually named NavHostFragment
.
As the user moves between destinations defined in the navigation graph, the navigation host Fragment swaps fragments in and out as necessary. The Fragment also creates and manages the appropriate Fragment back stack.
In this task, you modify your code to replace the TitleFragment
with the NavHostFragment
.
- Open res > layout > activity_main.xml and open the Code tab.
- In the
activity_main.xml
file, change the name of the existing title Fragment toandroidx.navigation.fragment.NavHostFragment
. - Change the ID to
myNavHostFragment
. - The navigation host Fragment needs to know which navigation graph resource to use. Add the
app:navGraph
attribute and set it to the navigation graph resource, which is@navigation/navigation
. - Add the
app:defaultNavHost
attribute and set it to"true"
. Now this navigation host is the default host and will intercept the system Back button.
Inside the activity_main.xml
layout file, your fragment
now looks like the following:
<!-- The NavHostFragment within the activity_main layout -->
<fragment
android:id="@+id/myNavHostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navGraph="@navigation/navigation"
app:defaultNavHost="true" />
5. Task: Add fragments to the navigation graph
In this task, you add the title Fragment and the game Fragment to the app's navigation graph. You connect the fragments to each other. Then you add a click handler to the Play button so that the user can navigate from the title screen to the game screen.
Step 1: Add two fragments to the navigation graph and connect them with an action
- Open navigation.xml from the navigation resource folder. In the Navigation Editor, click the New Destination button
. A list of fragments and activities appears.
- Select fragment_title. You add fragment_title first because the
TitleFragment
is where app users start when they first open the app.
- Use the New Destination button to add the GameFragment.
If the preview shows a "Preview Unavailable" message, click the Code tab to open the navigation XML. Make sure that the fragment
element for the gameFragment
includes tools:layout="@layout/fragment_game"
, as shown below.
<!-- The game fragment within the navigation XML, complete with tools:layout. -->
<fragment
android:id="@+id/gameFragment"
android:name="com.example.android.navigation.GameFragment"
android:label="GameFragment"
tools:layout="@layout/fragment_game" />
- In the layout editor (using the Design view), drag the
gameFragment
to the right so it doesn't overlap with the title Fragment.
- In the preview, hold the pointer over the title Fragment. A circular connection point appears on the right side of the Fragment view. Click the connection point and drag it to the
gameFragment
or drag it to anywhere in thegameFragment
preview. An Action is created that connects the two fragments. - To see the Action's attributes, click the arrow that connects the two fragments. In the Attributes pane, check that the action's ID is set to
action_titleFragment_to_gameFragment
.
Step 2: Add a click handler to the Play button
The title Fragment is connected to the game Fragment by an action. Now you want the Play button on the title screen to navigate the user to the game screen.
- In Android Studio, open the
TitleFragment.kt
file. Inside theonCreateView()
method, add the following code before thereturn
statement:
binding.playButton.setOnClickListener{}
- Inside
setOnClickListener
, add code to access the Play button through the binding class and navigate to the game fragment:
//The complete onClickListener with Navigation
binding.playButton.setOnClickListener { view : View ->
view.findNavController().navigate(R.id.action_titleFragment_to_gameFragment)
}
- Build the app and make sure that it has all the imports it needs. For example, you might need to add the following line to the
TitleFragment.kt
file:
import androidx.navigation.findNavController
- Run the app and tap the Play button on the title screen. The game screen opens.
6. Task: Add conditional navigation
In this step, you add conditional navigation, which is navigation that's only available to the user in certain contexts. A common use case for conditional navigation is when an app has a different flow, depending on whether the user is logged in.
Your app is a different case: Your app will navigate to a different Fragment, based on whether the user answers all the questions correctly.
The starter code contains two fragments for you to use in your conditional navigation:
- The
GameWonFragment
takes the user to a screen that shows a "Congratulations!" message. - The
GameOverFragment
takes the user to a screen that shows a "Try Again" message.
Step 1: Add GameWonFragment and GameOverFragment to the navigation graph
- Open the
navigation.xml
file, which is in thenavigation
folder. - To add the game-over Fragment to the navigation graph, click the New Destination button
in the Navigation Editor and select fragment_game_over.
- In the preview area of the layout editor, drag the game-over Fragment to the right of the game Fragment so the two don't overlap. Make sure to change the ID attribute of the game-over Fragment, to
gameOverFragment
. - To add the game-won Fragment to the navigation graph, click the New Destination button
and select fragment_game_won.
- Drag the game-won Fragment below the game-over Fragment so the two don't overlap. Make sure to name the ID attribute of the game-won Fragment as
gameWonFragment
.
The Layout Editor now looks something like the following screenshot:
Step 2: Connect the game Fragment to the game-result Fragment
In this step, you connect the game Fragment to both the game-won Fragment and the game-over Fragment.
- In the preview area of the Layout Editor, hold the pointer over the game Fragment until the circular connection point appears.
- Click the connection point and drag it to the game-over Fragment. A blue connection arrow appears, representing an Action that now connects the game Fragment to the game-over Fragment.
- In the same way, create an action that connects the game Fragment to the game-won Fragment. The Layout Editor now looks something like the following screenshot:
- In the preview, hold the pointer over the line that connects the game Fragment to the game-won Fragment. Notice that the ID for the Action has been assigned automatically.
Step 3: Add code to navigate from one Fragment to the next
GameFragment
is a Fragment class that contains questions and answers for the game. The class also includes logic that determines whether the user wins or loses the game. You need to add conditional navigation in the GameFragment
class, depending on whether the player wins or loses.
- Open the
GameFragment.kt
file. TheonCreateView()
method defines anif
/else
condition that determines whether the player has won or lost:
binding.submitButton.setOnClickListener @Suppress("UNUSED_ANONYMOUS_PARAMETER")
{
...
// answer matches, we have the correct answer.
if (answers[answerIndex] == currentQuestion.answers[0]) {
questionIndex++
// Advance to the next question
if (questionIndex < numQuestions) {
currentQuestion = questions[questionIndex]
setQuestion()
binding.invalidateAll()
} else {
// We've won! Navigate to the gameWonFragment.
}
} else {
// Game over! A wrong answer sends us to the gameOverFragment.
}
}
}
- Inside the
else
condition for winning the game, add the following code, which navigates to thegameWonFragment
. Make sure that the Action name (action_gameFragment_to_gameWonFragment
in this example) exactly matches what's set in thenavigation.xml
file.
// We've won! Navigate to the gameWonFragment.
view.findNavController()
.navigate(R.id.action_gameFragment_to_gameWonFragment)
- Inside the
else
condition for losing the game, add the following code, which navigates to thegameOverFragment
:
// Game over! A wrong answer sends us to the gameOverFragment.
view.findNavController().
navigate(R.id.action_gameFragment_to_gameOverFragment)
- Run the app and play the game by answering the questions. If you answer all three questions correctly, the app navigates to the
GameWonFragment
.
If you get any question wrong, the app immediately navigates to the GameOverFragment
.
The Android system's Back button is shown as 1 in the screenshot above. If the user presses the Back button in the game-won fragment or the game-over Fragment, the app navigates to the question screen. Ideally, the Back button should navigate back to the app's title screen. You change the destination for the Back button in the next task.
7. Task: Change the Back button's destination
The Android system keeps track of where users navigate on an Android-powered device. Each time the user goes to a new destination on the device, Android adds that destination to the back stack.
When the user presses the Back button, the app goes to the destination that's at the top of the back stack. By default, the top of the back stack is the screen that the user last viewed. The Back button is typically the left-most button at the bottom of the screen, as shown below. (The Back button's exact appearance is different on different devices.)
Until now, you've let the navigation controller handle the back stack for you. When the user navigates to a destination in your app, Android adds this destination to the back stack.
In the AndroidTrivia app, when the user presses the Back button from the GameOverFragment
or GameWonFragment
screen, they end up back in the GameFragment
. But you don't want to send the user to the GameFragment
, because the game is over. The user could restart the game, but a better experience would be to find themselves back at the title screen.
A navigation action can modify the back stack. In this task, you change the action that navigates from the game fragment so that the action removes the GameFragment
from the back stack. When the user wins or loses the game, if they press the Back button, the app skips the GameFragment
and goes back to the TitleFragment
.
Step 1: Set the pop behavior for the navigation actions
In this step, you manage the back stack so that when the user is at the GameWon
or GameOver
screen, pressing the Back button returns them to the title screen. You manage the back stack by setting the "pop" behavior for the actions that connect the fragments:
- The
popUpTo
attribute of an action "pops up" the back stack to a given destination before navigating. (Destinations are removed from the back stack.) - If the
popUpToInclusive
attribute isfalse
or is not set,popUpTo
removes destinations up to the specified destination, but leaves the specified destination in the back stack. - If
popUpToInclusive
is set totrue
, thepopUpTo
attribute removes all destinations up to and including the given destination from the back stack. - If
popUpToInclusive
istrue
andpopUpTo
is set to the app's starting location, the action removes all app destinations from the back stack. The Back button takes the user all the way out of the app.
In this step, you set the popUpTo
attribute for the two actions that you created in the previous task. You do this using the Pop To field in the Attributes pane of the Layout Editor.
- Open
navigation.xml
in the res > navigation folder. If the navigation graph does not appear in the layout editor, click the Design tab. - Select the action for navigating from the
gameFragment
to thegameOverFragment
. (In the preview area, the action is represented by a blue line that connects the two fragments.) - In the Attributes pane, set popUpTo to
gameFragment
. Select the popUpToInclusive checkbox.
This sets the popUpTo
and popUpToInclusive
attributes in the XML. The attributes tell the navigation component to remove fragments from the back stack up to and including GameFragment
. (This has the same effect as setting the popUpTo field to titleFragment
and clearing the popUpToInclusive checkbox.)
- Select the action for navigating from the
gameFragment
to thegameWonFragment
. Again, set popUpTo togameFragment
in the Attributes pane and select the popUpToInclusive checkbox.
- Run the app and play the game, then press the Back button. Whether you win or lose, the Back button takes you back to the
TitleFragment
.
Step 2: Add more navigation actions and add onClick handlers
Your app currently has the following user flow:
- The user plays the game and wins or loses, and the app navigates to the
GameWon
orGameOver
screen. - If the user presses the system Back button at this point, the app navigates to the
TitleFragment
. (You implemented this behavior in Step 1 of this task, above.)
In this step you implement two more steps of user flow:
- If the user taps the Next Match or Try Again button, the app navigates to the
GameFragment
screen. - If the user presses the system Back button at this point, the app navigates to the
TitleFragment
screen (instead of back to theGameWon
orGameOver
screen).
To create this user flow, use the popUpTo
attribute to manage the back stack:
- Inside the
navigation.xml
file, add a navigation action connectinggameOverFragment
togameFragment
. Make sure that the Fragment names in the action's ID match the Fragment names that are in the XML. For example, the action ID might beaction_gameOverFragment_to_gameFragment
.
- In the Attributes pane, set the action's popUpTo attribute to
titleFragment
. - Clear the popUpToInclusive checkbox, because you do not want the
titleFragment
to be included in the destinations that are removed from the back stack. Instead, you want everything up to thetitleFragment
(but not including it) to be removed from the back stack.
- Inside the
navigation.xml
file, add a navigation action connectinggameWonFragment
togameFragment
.
- For the action you just created, set the popUpTo attribute to
titleFragment
and clear the popUpToInclusive checkbox.
Now add functionality to the Try Again and Next Match buttons. When the user taps either button, you want the app to navigate to the GameFragment
screen so that the user can try the game again.
- Open the
GameOverFragment.kt
Kotlin file. At the end of theonCreateView()
method, before thereturn
statement, add the following code. The code adds a click listener to the Try Again button. When the user taps the button, the app navigates to the game Fragment.
// Add OnClick Handler for Try Again button
binding.tryAgainButton.setOnClickListener{view: View->
view.findNavController()
.navigate(R.id.action_gameOverFragment_to_gameFragment)}
- Open the
GameWonFragment.kt
Kotlin file. At the end of theonCreateView()
method, before thereturn
statement, add the following code:
// Add OnClick Handler for Next Match button
binding.nextMatchButton.setOnClickListener{view: View->
view.findNavController()
.navigate(R.id.action_gameWonFragment_to_gameFragment)}
- Run your app, play the game, and test the Next Match and Try Again buttons. Both buttons should take you back to the game screen so that you can try the game again.
- After you win or lose the game, tap Next Match or Try Again. Now press the system Back button. The app should navigate to the title screen, instead of going back to the screen that you just came from.
8. Task: Add an Up button in the app bar
The app bar
The app bar, sometimes called the action bar, is a dedicated space for app branding and identity. For example, you can set the app bar's color. The app bar gives the user access to familiar navigation features such as an options menu. To access the options menu from the app bar, the user taps the icon with the three vertical dots .
The app bar displays a title string that can change with each screen. For the title screen of the AndroidTrivia app, the app bar displays "Android Trivia." On the question screen, the title string also shows which question the user is on ("1/3," "2/3," or "3/3.")
The Up button
Currently in your app, the user uses the system Back button to navigate to previous screens. However, Android apps can also have an on-screen Up button that appears at the top left of the app bar.
In the AndroidTrivia app, you want the Up button to appear on every screen except the title screen. The Up button should disappear when the user reaches the title screen, because the title screen is at the top of the app's screen hierarchy.
Add support for an Up button
The navigation components include a UI library called NavigationUI
. The Navigation component includes a NavigationUI class. This class contains static methods that manage navigation with the top app bar, the navigation drawer, and bottom navigation. The navigation controller integrates with the app bar to implement the behavior of the Up button, so you don't have to do it yourself.
In the following steps, you use the navigation controller to add an Up button to your app:
- Open the
MainActivity.kt
kotlin file. Inside theonCreate()
method, add code to find the navigation controller object:
val navController = this.findNavController(R.id.myNavHostFragment)
- Also inside the
onCreate()
method, add code to link the navigation controller to the app bar:
NavigationUI.setupActionBarWithNavController(this,navController)
- After the
onCreate()
method, override theonSupportNavigateUp()
method to callnavigateUp()
in the navigation controller:
override fun onSupportNavigateUp(): Boolean {
val navController = this.findNavController(R.id.myNavHostFragment)
return navController.navigateUp()
}
- Run the app. The Up button appears in the app bar on every screen except the title screen. No matter where you are in the app, tapping the Up button takes you to the title screen.
You may see "fragment_title" in the upper left corner. Edit res>navigation>navigation.xml and change the label of com.example.android.navigation.TitleFragment
from "fragment_title" to @string/app_name. And then define this resource in res>values>strings.xml as "Android Trivia" and re-run the app.
9. Task: Add an options menu
Android has different types of menus, including the options menu. On modern Android devices, the user accesses the options menu by tapping three vertical dots that appear in the app bar.
In this task, you add an About menu item to the options menu. When the user taps the About menu item, the app navigates to the AboutFragment
, and the user sees information about how to use the app.
Step 1: Add the AboutFragment to the navigation graph
- Open the
navigation.xml
file and click the Design tab to see the navigation graph. - Click the New Destination button and select fragment_about.
- In the layout editor, drag the "about" Fragment to the left so it doesn't overlap with the other fragments. Make sure the Fragment's
ID
isaboutFragment
.
Step 2: Add the options-menu resource
- In the Android Studio Project pane, right-click the res folder and select New > Android Resource File.
- In the New Resource File dialog, name the file
options_menu
. - Select Menu as the Resource type and click OK.
- Open the
options_menu.xml
file from the res > menu folder and click the Design tab to see the Layout Editor. - From the Palette pane, drag a Menu Item (shown as 1 in the screenshot below) and drop it anywhere in the design editor pane (2). A menu item appears in the preview (3) and in the Component Tree (4).
- In the preview or in the Component Tree, click the menu item to show its attributes in the Attributes pane.
- Set the menu item's ID to aboutFragment. Set the title to @string/about.
Step 3: Add an onClick handler
In this step, you add code to implement behavior when the user taps the About menu item.
- Open the
TitleFragment.kt
Kotlin file. Inside theonCreateView()
method, before thereturn
, call thesetHasOptionsMenu()
method and pass intrue
.
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
...
setHasOptionsMenu(true)
return binding.root
}
- After the
onCreateView()
method, override theonCreateOptionsMenu()
method. In the method, add the options menu and inflate the menu resource file.
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.options_menu, menu)
}
- Override the
onOptionsItemSelected()
method to take the appropriate action when the menu item is tapped. In this case, the action is to navigate to the Fragment that has the sameid
as the selected menu item.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return NavigationUI.
onNavDestinationSelected(item,requireView().findNavController())
|| super.onOptionsItemSelected(item)
}
- If the app doesn't build, check to see whether you need to import packages to fix unresolved references in the code. For example, you can add
import android.view.*
to resolve several references (and replace more specific imports such asimport android.view.ViewGroup
). - Run the app and test the About menu item that's in the options menu. When you tap the menu item, the app should navigate to the "about" screen.
10. Task: Add the navigation drawer
In this task, you add a navigation drawer to the AndroidTrivia app. The navigation drawer is a panel that slides out from the edge of the screen. The drawer typically contains a header and a menu.
On phone-sized devices, the navigation drawer is hidden when not in use. Two types of user actions can make the navigation drawer appear:
- The drawer appears when the user swipes from the starting edge of the screen toward the ending edge of the screen. In the AndroidTrivia app, the navigation drawer appears when the user swipes from left to right.
- The drawer appears when the user is at the start destination of the app and taps the drawer icon in the app bar. (The drawer icon is sometimes called the nav drawer button or hamburger icon
.)
The screenshot below shows an open navigation drawer.
The navigation drawer is part of the Material Components for Android library, or Material library. You use the Material library to implement patterns that are part of Google's Material Design guidelines.
In your AndroidTrivia app, the navigation drawer will contain two menu items. The first item points to the existing "about" Fragment, and the second item will point to a new "rules" Fragment.
Step 1: Add the Material library to your project
- In the app-level Gradle build file, add the dependency for the Material library:
dependencies {
...
implementation "com.google.android.material:material:$version"
...
}
- Sync your project.
Step 2: Make sure the destination fragments have IDs
The navigation drawer will have two menu items, each representing a Fragment that can be reached from the navigation drawer. Both destinations must have an ID in the navigation graph.
The AboutFragment
already has an ID
in the navigation graph, but the RulesFragment
does not, so add it now:
- Open the
fragment_rules.xml
layout file to see what it looks like. Click the Design tab to look at the preview in the design editor. - Open the
navigation.xml
file in the Navigation Editor. Click the New Destination button and add the rules Fragment. Set its ID to rulesFragment.
Step 3: Create the drawer menu and the drawer layout
To create a navigation drawer, you create the navigation menu. You also need to put your views inside a DrawerLayout
in the layout file.
- Create the menu for the drawer. In the Project pane, right-click the res folder and select New Resource File. Name the file
navdrawer_menu
, set the resource type to Menu, and click OK. - Open
navdrawer_menu.xml
from the res > menu folder, then click the Design tab. Add two menu items by dragging them from the Palette pane into the Component Tree pane. - For the first menu item, set the id to rulesFragment. (The ID for a menu item should be the same as the ID for the Fragment.) Set the title to @string/rules and the icon to @drawable/rules.
- For the second menu item, set the id to aboutFragment, the title string to @string/about, and the icon to @drawable/about_android_trivia.
- Open the
activity_main.xml
layout file. To get all the drawer functionality for free, put your views inside aDrawerLayout
. Wrap the entire<LinearLayout>
inside a<DrawerLayout>
. (In other words, add aDrawerLayout
as the root view.)
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
. . .
</LinearLayout>
</androidx.drawerlayout.widget.DrawerLayout>
</layout>
- Now add the drawer, which is a
NavigationView
that uses thenavdrawer_menu
that you just defined. Add the following code in theDrawerLayout
, after the</LinearLayout>
element:
<com.google.android.material.navigation.NavigationView
android:id="@+id/navView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/nav_header"
app:menu="@menu/navdrawer_menu" />
Step 4: Display the navigation drawer
You created the menu items for the navigation drawer and the navigation drawer layout. Now you need to connect the navigation drawer to the navigation controller so that when users select items in the navigation drawer, the app navigates to the appropriate Fragment.
- Open the
MainActivity.kt
Kotlin file. InonCreate()
, add the code that allows the user to display the navigation drawer. Do this by callingsetupWithNavController()
. Add the following code at the bottom ofonCreate()
:
NavigationUI.setupWithNavController(binding.navView, navController)
- Run your app. Swipe from the left edge to display the navigation drawer, and make sure each of the menu items in the drawer goes to the right place.
Although the navigation drawer works, you need to fix one more thing. Typically apps also allow users to display the navigation drawer by tapping the drawer button (three lines) in the app bar on the home screen. Your app does not yet display the drawer button on the home screen.
Step 5: Display the navigation drawer from the drawer button
The final step is to enable the user to access the navigation drawer from the drawer button at the top left of the app bar.
- In the
MainActivity.kt
Kotlin file, add thelateinit
drawerLayout
member variable to represent the drawer layout:
private lateinit var drawerLayout: DrawerLayout
- Inside the
onCreate()
method, initializedrawerLayout
after thebinding
variable has been initialized.
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this,
R.layout.activity_main)
drawerLayout = binding.drawerLayout
- Add the
drawerLayout
as the third parameter to thesetupActionBarWithNavController()
method:
NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)
- Edit the
onSupportNavigateUp()
method to returnNavigationUI
.navigateUp
instead of returningnavController
.navigateUp
. Pass the navigation controller and the drawer layout tonavigateUp()
. The method will look like as follows:
override fun onSupportNavigateUp(): Boolean {
val navController = this.findNavController(R.id.myNavHostFragment)
return NavigationUI.navigateUp(navController, drawerLayout)
}
- You might need to add another import to the file so all the references resolve, for example:
import androidx.drawerlayout.widget.DrawerLayout
- Run your app. Swipe from the left edge to display the navigation drawer, and make sure each of the menu items in the drawer goes to the right place.
- Go to the home screen and tap the nav drawer button
to make sure the navigation drawer appears. Make sure that clicking the Rules or About options in the navigation drawer takes you to the right place.
Congratulations!
You have now added several different navigation options to your app.
The user can now progress through the app by playing the game. They can get back to the home screen at any time by using the Up button. They can get to the About screen either from the Options menu or from the navigation drawer. Pressing the Back button takes them back through previous screens in a way that makes sense for the app. The user can open the navigation drawer by swiping in from the left on any screen, or by tapping the drawer button in the app bar on the home screen.
Your app includes robust, logical navigation paths that are intuitive for your user to use. Congratulations!
11. Solution code
Android Studio project: AndroidTriviaNavigation
12. Summary
Navigation components
To use the Android navigation library, you need to do some setup:
- Add dependencies for
navigation-fragment-ktx
andnavigation-ui-ktx
in the module-levelbuild.gradle
file. - Add an
ext
variable for thenavigationVersion
in the project-levelbuild.gradle
file.
Navigation destinations are fragments, activities, or other app components that the user navigates to. A navigation graph defines the possible paths from one navigation destination to the next.
- To create a navigation graph, create a new Android resource file of type Navigation. This file defines the navigation flow through the app. The file is in the
res/navigation
folder, and it's typically callednavigation.xml
. - To see the navigation graph in the Navigation Editor, open the
navigation.xml
file and click the Design tab. - Use the Navigation Editor to add destinations such as fragments to the navigation graph.
- To define the path from one destination to another, use the Navigation Graph to create an action that connects the destinations. In the
navigation.xml
file, each of these connections is represented as anaction
that has anID
.
A navigation host Fragment, usually named NavHostFragment
, acts as a host for the fragments in the navigation graph:
- As the user moves between destinations defined in the navigation graph, the
NavHostFragment
swaps the fragments in and out and manages the Fragment back stack. - In the
activity_main.xml
layout file, theNavHostFragment
is represented by afragment
element with the nameandroid:name="androidx.navigation.fragment.NavHostFragment"
.
To define which Fragment is displayed when the user taps a view (for example a button), set the onClick
listener for the view:
- In the
onClick
listener, callfindNavController().navigate()
on the view. - Specify the
ID
of theaction
that leads to the destination.
Conditional navigation navigates to one screen in one case, and to a different screen in another case. To create conditional navigation:
- Use the Navigation Editor to create a connection from the starting Fragment to each of the possible destination fragments.
- Give each connection a unique ID.
- In the click-listener method for the
View
, add code to detect the conditions. Then callfindNavController().navigate()
on the view, passing in the ID for the appropriate action.
The Back button
The system's Back button is usually at the bottom of the device. By default, the Back button navigates the user back to the screen they viewed most recently. In some situations, you can control where the Back button takes the user:
- In the Navigation Editor, you can use the Attributes pane to change an action's popUpTo setting. This setting removes destinations from the back stack, which has the effect of determining where the Back button takes the user.
- The popUpTo setting appears as the
popUpTo
attribute in thenavigation.xml
file.
- Selecting the popUpToInclusive checkbox sets the
popUpToInclusive
attribute totrue
. All destinations up to and including this destination are removed from the back stack. - If an action's
popUpTo
attribute is set to the app's starting destination andpopUpToInclusive
is set totrue
, the Back button takes the user all the way out of the app.
The Up button
Screens in an Android app can have an on-screen Up button that appears at the top left of the app bar. (The app bar is sometimes called the action bar.) The Up button navigates "upwards" within the app's screens, based on the hierarchical relationships between screens.
The navigation controller's NavigationUI
library integrates with the app bar to allow the user to tap the Up button on the app bar to get back to the app's home screen from anywhere in the app.
To link the navigation controller to the app bar:
- In
onCreate()
, callsetupActionBarWithNavController()
on theNavigationUI
class, passing in the navigation controller:
val navController = this.findNavController(R.id.myNavHostFragment)
NavigationUI.setupActionBarWithNavController(this,navController)
- Override the
onSupportNavigateUp()
method to callnavigateUp()
in the navigation controller:
override fun onSupportNavigateUp(): Boolean {
val navController = this.findNavController(R.id.myNavHostFragment)
return navController.navigateUp()
}
}
The options menu
The options menu is a menu that the user accesses from the app bar by tapping the icon with the three vertical dots . To create an options menu with a menu item that displays a Fragment, make sure the Fragment has an ID. Then define the options menu and code the
onOptionsItemSelected()
handler for the menu items.
- Make sure the Fragment has an ID:
- Add the destination Fragment to the navigation graph and note the ID of the Fragment. (You can change the ID if you like.)
- Define the options menu:
- Create an Android resource file of type Menu, typically named
options_menu.xml
. The file is stored in the Res > Menu folder. - Open the
options_menu.xml
file in the design editor and drag a Menu Item widget from the Palette pane to the menu. - For convenience, make the ID of the menu item the same as the ID of the Fragment to display when the user clicks this menu item. This step is not required, but it makes it easier to code the
onClick
behavior for the menu item.
- Code the
onClick
handler for the menu item:
- In the Fragment or Activity that displays the options menu, in
onCreateView()
, callsetHasOptionsMenu(true)
to enable the options menu. - Implement
onCreateOptionsMenu()
to inflate the options menu:
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.options_menu, menu)
}
- Override the
onOptionsItemSelected()
method to take the appropriate action when the menu item is clicked. The following code displays the Fragment that has the same ID as the menu item. (This code only works if the menu item and the Fragment have identical ID values.)
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return NavigationUI.
onNavDestinationSelected(item,requireView().findNavController())
|| super.onOptionsItemSelected(item)
}
The navigation drawer
The navigation drawer is a panel that slides out from the edge of the screen. There are two ways for the user to open the navigation drawer:
- Swipe from the starting edge (usually the left) on any screen.
- Use the drawer button (three lines)
on the app bar at the top of the app.
To add a navigation drawer to your app:
- Add dependencies to
build.gradle (app)
. - Make sure each destination Fragment has an ID.
- Create the menu for the drawer.
- Add the drawer to the layout for the Fragment.
- Connect the drawer to the navigation controller.
- Set up the drawer button in the app bar.
These steps are explained in more detail below.
- Add dependencies to
build.gradle
:
- The navigation drawer is part of the Material Components for Android library. Add the Material library to the
build.gradle (app)
file:
dependencies {
...
implementation "com.google.android.material:material:$supportlibVersion"
...
}
- Give each destination Fragment an ID:
- If a Fragment is reachable from the navigation drawer, open it in the navigation graph to make sure that it has an ID.
- Create the menu for the drawer:
- Create an Android resource file of type Menu (typically called
navdrawer_menu
) for a navigation drawer menu. This creates a newnavdrawer_menu.xml
file in theRes > Menu
folder. - In the design editor, add Menu Item widgets to the Menu.
- Add the drawer to the layout for the Fragment:
- In the layout that contains the navigation host Fragment (which is typically the main layout), use
<androidx.drawerlayout.widget.DrawerLayout>
as the root view. - Add a
<com.google.android.material.navigation.NavigationView>
view to the layout.
- Connect the drawer to the navigation controller:
- Open the Activity that creates the navigation controller. (The main Activity is typically the one you want here.) In
onCreate()
, useNavigationUI.setupWithNavController()
to connect the navigation drawer with the navigation controller:
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(
this, R.layout.activity_main)
NavigationUI.setupWithNavController(binding.navView, navController)
- Set up the drawer button in the app bar:
- In
onCreate()
in the Activity that creates the navigation controller (which is typically the main Activity), pass the drawer layout as the third parameter toNavigationUI.setupActionBarWithNavController
:
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(
this, R.layout.activity_main)
NavigationUI.setupActionBarWithNavController(
this, navController, binding.drawerLayout)
- To make the Up button work with the drawer button, edit
onSupportNavigateUp()
to returnNavigationUI.navigateUp()
. Pass the navigation controller and the drawer layout tonavigateUp()
.
override fun onSupportNavigateUp(): Boolean {
val navController = this.findNavController(R.id.myNavHostFragment)
return NavigationUI.navigateUp(navController, drawerLayout)
}
13. Learn more
Udacity course:
Android developer documentation:
- Navigation Architecture Component
- Implement navigation with the navigation components
- Update UI components with NavigationUI
NavigationUI
NavHostFragment
- Designing Back and Up navigation
- Providing proper Back navigation
- Creating an options menu
- Create a navigation drawer
Material Design documentation:
14. Homework
This section lists possible homework assignments for students who are working through this codelab as part of a course led by an instructor. It's up to the instructor to do the following:
- Assign homework if required.
- Communicate to students how to submit homework assignments.
- Grade the homework assignments.
Instructors can use these suggestions as little or as much as they want, and should feel free to assign any other homework they feel is appropriate.
If you're working through this codelab on your own, feel free to use these homework assignments to test your knowledge.
Build and run an app
Add a Rules button and an About button to the layout for the TitleFragment
as shown below. When the user taps the Rules or About button, the app navigates to the RulesFragment
or AboutFragment
, as appropriate.
For extra credit:
On each of the layouts for the RulesFragment
and the AboutFragment
, add a Play button that navigates to the GameFragment
as shown here:
Hint: You need to update the layouts for these two fragments to enable data binding.
Change the back stack to enable the following user flow:
- The user taps the Play button to go from the
RulesFragment
screen or theAboutFragment
screen to theGameFragment
. - Then the user taps the Back button at the bottom of the screen.
- The app navigates back to the
TitleFragment
, not to theRulesFragment
orAboutFragment
.
In other words, when the user taps the Play button while viewing the rules or the information about the game, then taps the Back button, the RulesFragment
or the AboutFragment
is removed from the back stack.
Answer these questions
Question 1
How do you enable your project to use navigation components?
- Make sure every
Activity
class extends the classNavigationActivity
. - Use the
NavigationController
class as the launch Activity. - Add
<uses-navigation>
to the Android manifest file. - Add dependencies for
navigation-fragment-ktx
andnavigation-ui-ktx
in thebuild.gradle (module)
file.
Question 2
Where are the possible routes through your app defined?
- In a file (often called
navigation.xml)
in the res > layout folder. - In a file (often called
navigation.xml)
in the app > navigation folder. - In a file (often called
navigation.xml)
in the res > navigation folder. - In the
android-manifest.xml
file in the<navigation>
element.
Question 3
Which of the following statements about the NavHostFragment
are true? Select all that apply.
- As the user moves between destinations defined in the navigation graph, the
NavHostFragment
swaps the fragments in and out as necessary. - You can click the
NavHostFragment
in the Project view to open the navigation graph. - You add the
NavHostFragment
to the main layout by adding a<fragment>
whose name isandroidx.navigation.fragment.NavHostFragment
. - You must create a single
NavHostFragment
subclass and implement theonNavigate()
method to handle different kinds of navigation (such as button clicks).
Question 4
Which of the following statements about the navigation graph are true? Select all that apply.
- You create a potential path through the app from one Fragment to another by connecting the fragments in the navigation graph.
- The
type
of a connection between fragments isAction
. - You must add the
type="navigation"
attribute to every<fragment>
that is included in the navigation graph. - To open the navigation graph, you double-click the navigation file (
navigation.xml
) in the Android Studio Project pane, then click the Design tab.
Question 5
Where do you set the ID of a Fragment to be used in navigation?
- In the Fragment's layout file, either by setting the
ID
attribute in the design editor or in the layout XML file in the res > layout folder. - In the project's navigation file, either by setting the
ID
attribute in the navigation graph or in the navigation XML file in the res > navigation folder. - You need to set the ID in both the navigation file for the app and the layout file for the Fragment.
- Set the
ID
variable in the relevantFragment
class.
Question 6
The News app has a NewsFragment
that displays a Show headlines button. The goal is that when the user clicks this button, the app navigates to the HeadlinesFragment
.
Assume that you've added a connection from the NewsFragment
to the HeadlinesFragment
in the navigation graph, as shown here:
What else do you need to do so that when the user taps the Show headlines button, the app navigates to the HeadlinesFragment
?
- Actually, you don't need to do anything else. It's enough to set the navigation paths in the navigation graph.
- In the
onclickListener
for the Show headlines button, callnavigate()
on the navigation controller, passing the class name of the destination Fragment (in this caseHeadlinesFragment
) . - In the
onclickListener
for the Show headlines button, callnavigate()
on the navigation controller, passing the action that connects theNewsFragment
to theHeadlinesFragment
. - In the
onclickListener
for the Show headlines button, callnavigateTo()
on the container Fragment, passing the class name of the destination Fragment (in this caseHeadlinesFragment
) .
Question 7
When users navigate through an app, sometimes they want to retrace their steps back through the screens they have already visited.
Assume the following:
fragmentA
is connected tofragmentB
byaction_FragmentA_to_FragmentB
.fragmentB
is connected tofragmentC
byaction_FragmentB_to_FragmentC
Which of the following statements are true regarding navigating forward and backward through the app? (Choose all that apply.)
- The destination of the
action_FragmentA_to_FragmentB
action specifies that when the user is atFragmentA
, the next destination in the app isFragmentB
. - The destination of the
action_FragmentA_to_FragmentB
action sets the next destination that the user goes to, whether the user taps a button in the app or taps the Back button at the bottom of the screen. - The
popUpTo
attribute of the action can modify where the app navigates to if the user taps the system Back button. - The
popUpTo
attribute of the action can modify where the user goes next as they navigate forward through the app.
Question 8
When users navigate through an app, sometimes they want to retrace their steps back through the screens they have already visited. However, you can use the popUpTo
and popUpToInclusive
attributes of an action to modify the path backward through the app.
Assume the following:
fragmentA
is connected tofragmentB
byaction_FragmentA_to_FragmentB
.fragmentB
is connected tofragmentC
byaction_FragmentB_to_FragmentC
.
The user navigates from fragmentA
to fragmentB
to fragmentC
, then taps the system Back button. In this situation, let's say you want the app to navigate back to fragment
A
(instead of fragmentB
). What's the correct way to set the popUpTo
and popUpToInclusive
attributes?
- For
action_FragmentA_to_FragmentB
, setpopUpTo
tofragmentB
andpopUpToInclusive
to no value. Foraction_FragmentB_to_FragmentC
, setpopUpTo
tofragmentA
andpopUpToInclusive
totrue
. - For
action_FragmentA_to_FragmentB
, setpopUpTo
tofragmentA
andpopUpToInclusive
totrue
. Foraction_FragmentB_to_FragmentC
, setpopUpTo
tofragmentA
andpopUpToInclusive
totrue
. - For
action_FragmentA_to_FragmentB
, setpopUpTo
to none andpopUpToInclusive
to no value. (You can omit both attributes.) Foraction_FragmentB_to_FragmentC
, setpopUpTo
tofragmentA
andpopUpToInclusive
totrue
. - For
action_FragmentA_to_FragmentB
, setpopUpTo
to none andpopUpToInclusive
to no value. (You can omit both attributes.) Foraction_FragmentB_to_FragmentC
, setpopUpTo
tofragmentA
andpopUpToInclusive
tofalse.
Question 9
Assume that the action action_headlinesFragment_to_newsArticle
in the destination graph has a popUpTo
value of newsFragment
:
Assume that the user opens the app and navigates through the screens in the following sequence (without using the Back button):
Open app into News home > Headlines > News details
When the user is viewing the News detail screen, what happens If they tap the system Back button at the bottom of the screen?
Select all that apply (remembering that popUpTo
is newsFragment
).
- If
popUpToInclusive
istrue
: Android navigates out of the app because there is nothing left in the back stack for this app. - If
popUpToInclusive
isfalse
: The app goes back to the news home screen. - If
popUpToInclusive
istrue
: The app goes back to the news home screen. - If
popUpToInclusive
isfalse
: The app goes back to the headlines screen.
Question 10
Where do you define the items for a menu?
- It depends on where the menu will be shown. For a navigation drawer menu, add an
<item>
tag for each menu item in themenu
.xml
file in res > drawer folder. For the options menu, add an<item>
tag for each menu item in themenu
.xml
file in res > options folder. - In the layout file for the Fragment or Activity that displays the menu, add a
<menu>
tag that contains<item>
tags for each item. - In a
menu_name
.xml
file in the res > menu folder, add an<item>
tag for each menu item. Create separate XML files for each separate menu. - In the
android_manifest.xml
file, add a<menus>
tag that contains a<menu>
tag for each menu. For each<menu>
tag, add an<item>
tag for each menu item.
Question 11
To enable the options menu in your app, you need to define the menu items. Then what do you need to do in the Activity
or Fragment
where the options menu is to appear?
Select all that apply:
- Call
setHasOptionsMenu(true)
inonCreate()
for an Activity, or inonCreateView()
for a Fragment. - Implement
onCreateOptionsMenu()
in the Activity or Fragment to create the menu. - Set the
onClick
attribute in the menu's XML file toonShowOptionsMenu
, unless you are implementing a customonClick
listener for the options menu, in which case specify the name of the customonClick
listener instead. - Implement
onOptionsItemSelected()
in the Activity or Fragment to determine what happens when a user selects a menu item in the options menu.
Question 12
What do you need to do to enable a navigation drawer in your app? You can assume that your project is using the navigation graph and that you've already defined the menu items.
Select all that apply:
- Use
<DrawerLayout>
as the root view in the relevant layout file, and add a <NavigationView>
tag to that layout. - Use
<Navigation>
as the root view in the relevant layout file, and add a <NavigationView>
tag to that layout. - In the
<NavigationView>
in the layout, set theandroid:menu
attribute to the navigation drawer menu. - In the navigation XML file, make sure that the navigation menu has an ID.
Question 13
Following on from the previous question, you need to write some code to enable the navigation drawer to be displayed when the user swipes in from the left side of the screen?
In onCreate()
within the Activity
that creates the navigation controller, what is the right code to add?
- A:
NavigationUI.setupWithNavController(
navigationLayoutID, navigationMenuID)
- B:
NavigationUI.setupWithNavController(
navigationView, navigationController)
- C:
NavigationDrawer.setupWithNavInterface(
navigationView, navigationController)
- D:
NavigationDrawer.setupWithNavController(
navigationView, navigationMenuID)
Question 14
How do you add support for the Up button at the top of the screen to enable users to get back to the app's home screen from anywhere in the app? What do you need to do in the relevant Activity
?
Select all that apply:
- In the
res > menu
folder, create theup_menu.xml
file. - Link the navigation controller to the app bar using
NavigationUI.setupActionBarWithNavController(context,navigationController)
- Override
onSupportNavigateUp()
method to callnavigateUp()
on the navigation controller. - In the navigation graph, make sure the starting Fragment has an
ID
ofHomeFragment
.
Submit your app for grading
Guidance for graders
Check that the app has the following features:
- The
TitleFragment
screen has a Rules and About button as shown below. - When the user taps the Rules or About button, the app navigates to the
RulesFragment
orAboutFragment
, as appropriate.
For extra credit:
The RulesFragment
and AboutFragment
screens both have a Play button that navigates to the GameFragment
as shown here:
Check that the layouts for these two fragments both have data binding enabled.
Check the following user flow:
- Tap the Play button to go from the rules-fragment screen or the about-fragment screen to the game Fragment.
- Press the system Back button at the bottom of the screen.
The app should navigate back to the title Fragment, not to the rules Fragment or the about Fragment.
15. Next codelab
For links to other codelabs in this course, see the Android Kotlin Fundamentals codelabs landing page.