Android Kotlin 基础知识:03.2 定义导航路径

此 Codelab 是“Android Kotlin 基础知识”课程的一部分。如果您按顺序学习这些 Codelab,您将会充分发掘此课程的价值。“Android Kotlin 基础知识”Codelab 着陆页列出了所有课程 Codelab。

在上一个 Codelab 中,您修改了 AndroidTrivia 应用,以向现有 activity 添加 fragment。在此 Codelab 中,您将向该应用添加导航。

导航简介

打造用户在应用中导航的体验一直是开发者感兴趣的话题。对于 Android 应用,导航架构组件可让您更轻松地实现导航。

目的地是指用户可以在应用中导航到的任意位置。应用的导航图由应用中的一组目的地组成。通过导航图,您可以直观地定义和自定义用户如何在应用中的目的地之间导航。

您应当已掌握的内容

  • Kotlin 的基础知识
  • 如何使用 Kotlin 创建基本 Android 应用
  • 如何使用布局

学习内容

  • 如何使用导航图
  • 如何在应用中定义导航路径
  • 什么是向上按钮,以及如何添加一个向上按钮
  • 如何创建选项菜单
  • 如何创建抽屉式导航栏

实践内容

  • 使用导航库和 Navigation Editor 为 fragment 创建导航图。
  • 在应用中创建导航路径。
  • 使用选项菜单添加导航。
  • 实现一个向上按钮,以便用户可以从应用中的任意位置导航回标题屏幕。
  • 添加抽屉式导航栏菜单。

您在上一个 Codelab 中开始使用的 AndroidTrivia 应用是一款游戏,用户可以在其中回答与 Android 开发相关的问题。如果用户答对三个问题,他们就赢了游戏。

如果您已完成上一个 Codelab,请将该代码用作此 Codelab 的起始代码。否则,请从 GitHub 下载 AndroidTriviaFragment 应用以获取起始代码。

在此 Codelab 中,您将通过以下方式更新 AndroidTrivia 应用:

  • 您将为该应用创建导航图。
  • 您将为标题屏幕和游戏屏幕添加导航。
  • 您会通过一项操作将这两个屏幕连接起来,并让用户能够通过点按 Play 导航到游戏屏幕。
  • 您将添加一个向上按钮,该按钮在某些屏幕的顶部显示为向左箭头。

f5d12f1802429b4d.png 8f67b0c6679e3ad2.png 70bd954293047b9a.png

第 1 步:添加导航依赖项

Navigation 组件是一个库,可管理应用中多个屏幕之间的复杂导航、过渡动画、深层链接以及编译时检查参数传递。

如需使用导航库,您需要将导航依赖项添加到 Gradle 文件中。

  1. 首先,下载 AndroidTriviaFragment 起始应用,或使用您自己的 AndroidTrivia 应用副本(来自上一个 Codelab)。在 Android Studio 中打开 AndroidTrivia 应用。
  2. 在“Project”的“Android”窗格中,打开 Gradle Scripts 文件夹。双击项目级 build.gradle 文件以打开该文件。

b18cbeb80151e422.png

  1. 在项目级 build.gradle 文件的顶部,除了其他 ext 变量以外,为 navigationVersion 添加一个变量。如需查找最新的导航版本号,请参阅 Android 开发者文档中的声明依赖项
ext {
        ...
        navigationVersion = "2.3.0"
        ...
    }
  1. Gradle Scripts 文件夹中,打开模块级 build.gradle 文件。添加 navigation-fragment-ktxnavigation-ui-ktx 的依赖项,如下所示:
dependencies {
  ...
  implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion"
  implementation "androidx.navigation:navigation-ui-ktx:$navigationVersion"
  ...
}
  1. 重新构建项目。

第 2 步:将导航图添加到项目中

  1. 在“Project”的“Android”窗格中,右键点击 res 文件夹,然后依次选择 New > Android Resource File
  2. New Resource File 对话框中,选择 Navigation 作为 Resource type
  3. File name 字段中,将文件命名为 navigation
  4. 确保 Chosen qualifiers 框为空,并点击 OK。此时,res > navigation 文件夹中会出现一个新文件 navigation.xml20dce46c90407e47.png
  5. 打开 res > navigation > navigation.xml 文件,然后点击 Design 标签页以打开 Navigation Editor。请注意布局编辑器中的 No NavHostFragments found 消息。您将在下一项任务中解决此问题。a400b509131dfe7.png

导航宿主 fragment 充当导航图中 fragment 的宿主。导航宿主 fragment 通常名为 NavHostFragment

当用户在导航图中定义的目的地之间移动时,导航宿主 fragment 会根据需要换入或换出 fragment。该 fragment 还会创建和管理相应的 fragment 返回堆栈。

在此任务中,您将修改代码以将 TitleFragment 替换为 NavHostFragment

  1. 打开 res > layout > activity_main.xml,然后打开 Code 标签页。
  2. activity_main.xml 文件中,将现有标题 fragment 的名称更改为 androidx.navigation.fragment.NavHostFragment
  3. 将 ID 更改为 myNavHostFragment
  4. 导航宿主 fragment 需要知道要使用的导航图资源。添加 app:navGraph 属性,并将其设置为相应的导航图资源,即 @navigation/navigation
  5. 添加 app:defaultNavHost 属性,并将其设置为 "true"。现在,此导航宿主是默认宿主,并将拦截系统返回按钮。

activity_main.xml 布局文件内,您的 fragment 现在如下所示:

<!-- 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" />

在此任务中,您将向应用的导航图中添加标题 fragment 和游戏 fragment。您会将这两个 fragment 相互连接。然后,您将向 Play 按钮添加点击处理程序,以便用户可以从标题屏幕导航到游戏屏幕。

第 1 步:向导航图中添加两个 fragment 并通过一项操作将其连接起来

  1. navigation 资源文件夹中打开 navigation.xml。在 Navigation Editor 中,点击 New Destination 按钮 81d275f3f278af81.png。此时将显示 fragment 和 activity 的列表。

f90c10a57114cf2b.png

  1. 选择 fragment_title。您首先添加 fragment_title,因为 TitleFragment 是应用用户首次打开应用时开始的位置。

54e5a268b4167c87.png

a881f5057f83b8b9.png

  1. 使用 New Destination 按钮添加 GameFragment

如果预览显示“Preview Unavailable”消息,请点击 Code 标签页以打开导航 XML。确保 gameFragmentfragment 元素包含 tools:layout="@layout/fragment_game",如下所示。

<!-- 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" />
  1. 在布局编辑器中(使用 Design 视图),将 gameFragment 拖动到右侧,以使其不会与标题 fragment 重叠。

493b9722841a7742.png

  1. 在预览中,将指针悬停在标题 fragment 上。此时,fragment 视图右侧会出现一个圆形连接点。点击该连接点,并将其拖动到 gameFragment 或将其拖动到 gameFragment 预览中的任意位置。这样就创建了连接这两个 fragment 的操作。
  2. 如需查看该操作的属性,请点击连接这两个 fragment 的箭头。在 Attributes 窗格中,检查该操作的 ID 是否设置为 action_titleFragment_to_gameFragment66b05cb09acab955.png

第 2 步:向“Play”按钮添加点击处理程序

标题 fragment 通过一项操作与游戏 fragment 连接起来。现在,您希望用户点击标题屏幕上的 Play 按钮时会导航到游戏屏幕。

  1. 在 Android Studio 中,打开 TitleFragment.kt 文件。在 onCreateView() 方法内,在 return 语句前面添加以下代码:
binding.playButton.setOnClickListener{}
  1. setOnClickListener 内,添加相应的代码以通过绑定类访问 Play 按钮,并导航到游戏 fragment:
//The complete onClickListener with Navigation
binding.playButton.setOnClickListener { view : View ->
       view.findNavController().navigate(R.id.action_titleFragment_to_gameFragment)
}
  1. 构建应用,并确保它具有需要的所有导入内容。例如,您可能需要将以下代码行添加到 TitleFragment.kt 文件中:
import androidx.navigation.findNavController
  1. 运行应用,并点按标题屏幕上的 Play 按钮。此时将打开游戏屏幕。38b676ecf8d6c29a.png

在此步骤中,您将添加条件导航,这是一种只在特定情况下可供用户使用的导航。条件导航的一个常见用例是当应用的流程根据用户是否已登录而有所不同时。

您的应用的情况有所不同:您的应用将根据用户是否答对所有问题而导航到不同的 fragment。

起始代码包含两个 fragment 可供您在条件导航中使用:

  • GameWonFragment 可使用户转到显示“Congratulations!”消息的屏幕。
  • GameOverFragment 可使用户转到显示“Try Again”消息的屏幕。

第 1 步:向导航图中添加 GameWonFragment 和 GameOverFragment

  1. 打开 navigation 文件夹中的 navigation.xml 文件。
  2. 如需向导航图中添加游戏结束 fragment,请点击 Navigation Editor 中的 New Destination 按钮 3f7501a427b82d14.png,然后选择 fragment_game_overd9f99185106728bc.png
  3. 在布局编辑器的预览区域中,将游戏结束 fragment 拖动到游戏 fragment 的右侧,以使这两者不会重叠。确保将游戏结束 fragment 的 ID 属性更改为 gameOverFragment
  4. 如需向导航图中添加游戏胜利 fragment,请点击 New Destination 按钮 3f7501a427b82d14.png,然后选择 fragment_game_won

8b59562e5ae0df80.png

  1. 将游戏胜利 fragment 拖动到游戏结束 fragment 的下方,以使这两者不会重叠。确保将游戏胜利 fragment 的 ID 属性命名为 gameWonFragment

现在,布局编辑器类似于以下屏幕截图:d1ce5d45da96549c.png

第 2 步:将游戏 fragment 连接到游戏结果 fragment

在此步骤中,您会将游戏 fragment 同时连接到游戏胜利 fragment 和游戏结束 fragment。

  1. 在布局编辑器的预览区域中,将指针悬停在游戏 fragment 上,直到出现圆形连接点。
  2. 点击该连接点并将其拖动到游戏结束 fragment。此时将出现一个蓝色连接箭头,它表示一项操作,该操作现在将游戏 fragment 连接到游戏结束 fragment。
  3. 以同样的方式,创建一项将游戏 fragment 连接到游戏胜利 fragment 的操作。现在,布局编辑器类似于以下屏幕截图:d4cc27a71718e742.png
  4. 在预览中,将指针悬停在将游戏 fragment 连接到游戏胜利 fragment 的线上。请注意,系统自动为该操作分配了 ID。

第 3 步:添加相应的代码以从一个 fragment 导航到下一个 fragment

GameFragment 是一个 fragment 类,其中包含游戏的问题和答案。该类还包含用于确定用户是赢了还是输了游戏的逻辑。您需要在 GameFragment 类中添加条件导航,这取决于玩家是赢了还是输了。

  1. 打开 GameFragment.kt 文件。onCreateView() 方法定义了一个 if/else 条件,用于确定玩家是赢了还是输了:
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.
                }
            }
        }
  1. 在赢了游戏的 else 条件内,添加以下代码,这样会使应用导航到 gameWonFragment。确保操作名称(在本例中为 action_gameFragment_to_gameWonFragment)与 navigation.xml 文件中设置的完全匹配。
// We've won!  Navigate to the gameWonFragment.
view.findNavController()
   .navigate(R.id.action_gameFragment_to_gameWonFragment)
  1. 在输了游戏的 else 条件内,添加以下代码,这样会使应用导航到 gameOverFragment
// Game over! A wrong answer sends us to the gameOverFragment.
view.findNavController().
   navigate(R.id.action_gameFragment_to_gameOverFragment)
  1. 运行应用并通过回答问题来玩游戏。如果您答对全部三个问题,应用会导航到 GameWonFragment

60a5222e025dee8a.png

如果您答错任何问题,应用会立即导航到 GameOverFragment

1c1f2a6ada154c8a.png

Android 系统的返回按钮如上面屏幕截图中的标注 1 所示。如果用户在游戏胜利 fragment 或游戏结束 fragment 中按返回按钮,应用会导航到问题屏幕。理想情况下,用户按返回按钮后应导航回应用的标题屏幕。您将在下一项任务中更改返回按钮的目的地。

Android 系统会跟踪用户在 Android 设备上的导航位置。每次用户在设备上转到一个新目的地时,Android 都会将该目的地添加到返回堆栈。

当用户按返回按钮时,应用会转到返回堆栈顶部的目的地。默认情况下,返回堆栈顶部是用户上次查看的屏幕。返回按钮通常是屏幕底部最左侧的按钮,如下所示。(返回按钮的确切外观在不同的设备上有所不同。)

19d528c831bc36e1.png

到目前为止,您一直让导航控制器为您处理返回堆栈。当用户在应用中导航到某个目的地时,Android 会将该目的地添加到返回堆栈。

在 AndroidTrivia 应用中,当用户从 GameOverFragmentGameWonFragment 屏幕中按返回按钮时,他们最终会返回 GameFragment。但您不想让用户转到 GameFragment,因为游戏已经结束。用户可以重新开始游戏,但更好的体验是让他们返回标题屏幕。

导航操作可以修改返回堆栈。在此任务中,您将更改从游戏 fragment 导航的操作,以使该操作从返回堆栈中移除 GameFragment。当用户赢了或输了游戏时,如果他们按返回按钮,应用会跳过 GameFragment 而返回 TitleFragment

第 1 步:为导航操作设置弹出行为

在此步骤中,您将管理返回堆栈,以使用户在 GameWonGameOver 屏幕中按返回按钮时会返回标题屏幕。您可以通过为连接 fragment 的操作设置“弹出”行为来管理返回堆栈:

  • 操作的 popUpTo 属性会在导航之前将返回堆栈“弹出”到给定的目的地。(系统会从返回堆栈中移除目的地。)
  • 如果 popUpToInclusive 属性为 false 或未设置,则 popUpTo 会移除直到指定目的地的目的地,但会将指定目的地留在返回堆栈中。
  • 如果 popUpToInclusive 设置为 true,则 popUpTo 属性会从返回堆栈中移除直到并包括给定目的地在内的所有目的地。
  • 如果 popUpToInclusivetruepopUpTo 设置为应用的起始位置,则操作会从返回堆栈中移除所有应用目的地。用户按返回按钮后会直接退出应用。

在此步骤中,您将为在上一项任务中创建的两项操作设置 popUpTo 属性。您将使用布局编辑器的 Attributes 窗格中的 Pop To 字段来执行此操作。

  1. res > navigation 文件夹中打开 navigation.xml。如果布局编辑器中未显示导航图,请点击 Design 标签页。
  2. 选择从 gameFragment 导航到 gameOverFragment 的操作。(在预览区域中,该操作由一条连接这两个 fragment 的蓝线表示。)
  3. Attributes 窗格中,将 popUpTo 设置为 gameFragment。选中 popUpToInclusive 复选框。

7a65cf0c73c9904d.png

这样会在 XML 中设置 popUpTopopUpToInclusive 属性。这些属性会告知导航组件从返回堆栈中移除直到并包括 GameFragment 在内的 fragment。(这样做与将 popUpTo 字段设置为 titleFragment 并清除 popUpToInclusive 复选框的效果相同。)

  1. 选择从 gameFragment 导航到 gameWonFragment 的操作。同样,在 Attributes 窗格中将 popUpTo 设置为 gameFragment 并选中 popUpToInclusive 复选框。

ca0b60b19e24864f.png

  1. 运行应用并玩游戏,然后按返回按钮。无论您是赢了还是输了,按返回按钮后都会返回 TitleFragment

第 2 步:添加更多导航操作并添加 onClick 处理程序

您的应用目前具有以下用户流:

  • 用户玩游戏赢了或输了之后,应用会导航到 GameWonGameOver 屏幕。
  • 如果用户此时按系统返回按钮,应用会导航到 TitleFragment。(您在上面此任务的第 1 步中实现了此行为。)

在此步骤中,您将实现用户流的另外两个步骤:

  • 如果用户点按 Next MatchTry Again 按钮,应用会导航到 GameFragment 屏幕。
  • 如果用户此时按系统返回按钮,应用会导航到 TitleFragment 屏幕(而不是返回 GameWonGameOver 屏幕)。

如需创建此用户流,请使用 popUpTo 属性管理返回堆栈:

  1. navigation.xml 文件内,添加将 gameOverFragment 连接到 gameFragment 的导航操作。请确保操作 ID 中的 fragment 名称与 XML 中的 fragment 名称相符。例如,操作 ID 可能是 action_gameOverFragment_to_gameFragment

b3ba52c4de77caf2.png

  1. Attributes 窗格中,将操作的 popUpTo 属性设置为 titleFragment
  2. 清除 popUpToInclusive 复选框,因为您不想让 titleFragment 包含在从返回堆栈中移除的目的地中,而是希望从返回堆栈中移除直到 titleFragment 的所有目的地(但不包括该 fragment)。

f7a3e88d99887fd8.png

  1. navigation.xml 文件内,添加将 gameWonFragment 连接到 gameFragment 的导航操作。

593921dbf00365c1.png

  1. 对于您刚刚创建的操作,将 popUpTo 属性设置为 titleFragment,并清除 popUpToInclusive 复选框。

1f8e86b02d795270.png

现在,向 Try AgainNext Match 按钮添加功能。当用户点按任一按钮时,您希望应用导航到 GameFragment 屏幕,这样用户就可以再次玩游戏。

  1. 打开 GameOverFragment.kt Kotlin 文件。在 onCreateView() 方法的末尾,在 return 语句前面,添加以下代码。该代码会向 Try Again 按钮添加点击监听器。当用户点按该按钮时,应用会导航到游戏 fragment。
// Add OnClick Handler for Try Again button
        binding.tryAgainButton.setOnClickListener{view: View->
        view.findNavController()
                .navigate(R.id.action_gameOverFragment_to_gameFragment)}
  1. 打开 GameWonFragment.kt Kotlin 文件。在 onCreateView() 方法的末尾,在 return 语句前面,添加以下代码:
// Add OnClick Handler for Next Match button
        binding.nextMatchButton.setOnClickListener{view: View->
            view.findNavController()
                    .navigate(R.id.action_gameWonFragment_to_gameFragment)}
  1. 运行应用,玩游戏,并测试 Next MatchTry Again 按钮。点按这两个按钮后都应返回游戏屏幕,这样您就可以再次玩游戏。
  2. 在您赢了或输了游戏之后,点按 Next MatchTry Again。现在,按系统返回按钮。应用应导航到标题屏幕,而不是返回您刚刚来自的屏幕。

应用栏

应用栏有时称为操作栏,它是应用品牌信息和身份信息的专用区域。例如,您可以设置应用栏的颜色。应用栏让用户能够访问熟悉的导航功能,如选项菜单。如需从应用栏访问选项菜单,用户可以点按带有三个垂直点的图标 4cdd17fa43bfbe6.png

应用栏会显示一个标题字符串,该字符串会随每个屏幕而发生变化。对于 AndroidTrivia 应用的标题屏幕,应用栏会显示“Android Trivia”。在问题屏幕上,标题字符串还会显示用户正在回答第几个问题(“1/3”、“2/3”或“3/3”)。

向上按钮

目前在您的应用中,用户使用系统返回按钮导航到之前的屏幕。不过,Android 应用也可以有屏幕上的向上按钮,该按钮将显示在应用栏的左上角。

在 AndroidTrivia 应用中,您希望向上按钮显示在除标题屏幕之外的所有屏幕上。当用户到达标题屏幕时,向上按钮应消失,因为标题屏幕位于应用的屏幕层次结构的顶部。

添加对向上按钮的支持

导航组件包含一个称为 NavigationUI 的界面库。Navigation 组件包含一个 NavigationUI 类。此类包含多种静态方法,可帮助您使用顶部应用栏、抽屉式导航栏和底部导航栏来管理导航。导航控制器与应用栏相集成来实现向上按钮的行为,所以您不必自己去做。

在以下步骤中,您将使用导航控制器向应用中添加向上按钮:

  1. 打开 MainActivity.kt Kotlin 文件。在 onCreate() 方法内,添加相应的代码以查找导航控制器对象:
val navController = this.findNavController(R.id.myNavHostFragment)
  1. 此外,在 onCreate() 方法内,添加相应的代码以将导航控制器关联到应用栏:
NavigationUI.setupActionBarWithNavController(this,navController)
  1. onCreate() 方法之后,替换 onSupportNavigateUp() 方法以在导航控制器中调用 navigateUp()
override fun onSupportNavigateUp(): Boolean {
        val navController = this.findNavController(R.id.myNavHostFragment)
        return navController.navigateUp()
    }
  1. 运行应用。向上按钮将显示在除标题屏幕之外的所有屏幕的应用栏中。无论您在应用中的什么位置,点按向上按钮后都会转到标题屏幕。

您可能会在左上角看到“fragment_title”。修改 res>navigation>navigation.xml,将 com.example.android.navigation.TitleFragment 的标签从“fragment_title”更改为 @string/app_name。然后,在 res>values>strings.xml 中将此资源定义为“Android Trivia”,并重新运行应用。

ede7ea1d73b46ab0.png

Android 具有不同类型的菜单,包括选项菜单。在现代 Android 设备上,用户通过点按显示在应用栏中的三个垂直点 4cdd17fa43bfbe6.png 来访问选项菜单。

在此任务中,您将向选项菜单中添加 About 菜单项。当用户点按 About 菜单项时,应用会导航到 AboutFragment,用户会看到有关如何使用应用的信息。

第 1 步:向导航图中添加 AboutFragment

  1. 打开 navigation.xml 文件,然后点击 Design 标签页以查看导航图。
  2. 点击 New Destination 按钮,然后选择 fragment_about2d78693674c34dda.png
  3. 在布局编辑器中,将“about”fragment 拖动到左侧,以使其不会与其他 fragment 重叠。确保该 fragment 的 IDaboutFragment

97459cd8e9616164.png

第 2 步:添加选项菜单资源

  1. 在 Android Studio 的“Project”窗格中,右键点击 res 文件夹,然后依次选择 New > Android Resource File
  2. New Resource File 对话框中,将文件命名为 options_menu
  3. 选择 Menu 作为 Resource type,然后点击 OK3c841c2d34ef8379.png
  4. res > menu 文件夹中打开 options_menu.xml 文件,然后点击 Design 标签页以查看布局编辑器。
  5. Palette 窗格中,将 Menu Item(如下面屏幕截图中的标注 1 所示)拖放到设计编辑器窗格 (2) 中的任意位置。此时,一个菜单项会出现在预览 (3) 和 Component Tree (4) 中。345be27047f9c8ad.png
  6. 在预览或 Component Tree 中,点击该菜单项以在 Attributes 窗格中显示其属性。
  7. 将该菜单项的 ID 设置为 aboutFragment。将标题设置为 @string/about242ec5091ed2c2a5.png

第 3 步:添加 onClick 处理程序

在此步骤中,您将添加相应的代码以实现用户点按 About 菜单项时的行为。

  1. 打开 TitleFragment.kt Kotlin 文件。在 onCreateView() 方法内的 return 之前,调用 setHasOptionsMenu() 方法,并传入 true
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                         savedInstanceState: Bundle?): View? {
   ...
   setHasOptionsMenu(true)
   return binding.root
}
  1. onCreateView() 方法之后,替换 onCreateOptionsMenu() 方法。在该方法中,添加选项菜单,并膨胀菜单资源文件。
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        super.onCreateOptionsMenu(menu, inflater)
        inflater.inflate(R.menu.options_menu, menu)
}
  1. 替换 onOptionsItemSelected() 方法以在用户点按菜单项时采取适当的操作。在本例中,操作是导航到与选定的菜单项具有相同 id 的 fragment。
override fun onOptionsItemSelected(item: MenuItem): Boolean {
     return NavigationUI.
            onNavDestinationSelected(item,requireView().findNavController())
            || super.onOptionsItemSelected(item)
}
  1. 如果应用未构建,请检查是否需要导入软件包来修复代码中未解析的引用。例如,您可以添加 import android.view.* 来解析几项引用(还可以替换更具体的导入,如 import android.view.ViewGroup)。
  2. 运行应用,并测试选项菜单中的 About 菜单项。当您点按该菜单项时,应用应导航到“about”屏幕。

c1c134d86b99a747.png

在此任务中,您将向 AndroidTrivia 应用中添加抽屉式导航栏。抽屉式导航栏是从屏幕边缘滑出的一个面板。抽屉式导航栏通常包含一个标题和一个菜单。

在手机大小的设备上,抽屉式导航栏在不使用时会隐藏起来。两种类型的用户操作可使抽屉式导航栏出现:

  • 当用户从屏幕的起始边缘向屏幕的结束边缘滑动时,抽屉式导航栏会出现。在 AndroidTrivia 应用中,当用户从左向右滑动时,抽屉式导航栏会出现。
  • 当用户位于应用的起始目的地并点按应用栏中的抽屉式导航栏图标时,抽屉式导航栏会出现。(抽屉式导航栏图标有时称为抽屉式导航栏按钮或汉堡图标 7277f85db3a1ad13.png。)

下面的屏幕截图显示了一个打开的抽屉式导航栏。

4c30d26a5ae3c341.png

抽屉式导航栏是 Material Components for Android 库(即 Material 库)的一部分。您可以使用 Material 库实现符合 Google 的 Material Design 准则的模式。

在 AndroidTrivia 应用中,抽屉式导航栏将包含两个菜单项。第一项指向现有的“about”fragment,第二项将指向新的“rules”fragment。

455051aa6083ccfe.png

第 1 步:将 Material 库添加到项目中

  1. 在应用级 Gradle build 文件中,添加 Material 库的依赖项:
dependencies {
    ...
    implementation "com.google.android.material:material:$version"
    ...
}
  1. 同步您的项目。

第 2 步:确保目的地 fragment 具有 ID

抽屉式导航栏将有两个菜单项,每个菜单项表示可以从抽屉式导航栏到达的一个 fragment。这两个目的地都必须在导航图中具有 ID。

AboutFragment 已经在导航图中具有 ID,但 RulesFragment 却没有,所以我们现在来添加:

  1. 打开 fragment_rules.xml 布局文件,看看它是什么样子。点击 Design 标签页,以在设计编辑器中查看预览。
  2. 在 Navigation Editor 中打开 navigation.xml 文件。点击 New Destination 按钮,然后添加规则 fragment。将其 ID 设置为 rulesFragment

2417ae056f087b1b.png

22127c2a9dd49b15.png

第 3 步:创建抽屉式导航栏菜单和抽屉式导航栏布局

如需创建抽屉式导航栏,您需要创建导航菜单。您还需要在布局文件中将视图放在 DrawerLayout 内。

  1. 创建抽屉式导航栏的菜单。在“Project”窗格中,右键点击 res 文件夹,然后选择 New Resource File。将文件命名为 navdrawer_menu,将资源类型设置为 Menu,然后点击 OKa925786508c5b763.png
  2. res > menu 文件夹中打开 navdrawer_menu.xml,然后点击 Design 标签页。添加两个菜单项,方法是将其从 Palette 窗格拖动到 Component Tree 窗格中。
  3. 对于第一个菜单项,将 id 设置为 rulesFragment。(菜单项的 ID 应与 fragment 的 ID 相同。)将 title 设置为 @string/rules,并将 icon 设置为 @drawable/rules

b2e6e3aa1b38f9ae.png

  1. 对于第二个菜单项,将 id 设置为 aboutFragment,将 title 字符串设置为 @string/about,并将图标设置为 @drawable/about_android_trivia

247150f27cfb0ada.png

  1. 打开 activity_main.xml 布局文件。如需免费获得所有抽屉式导航栏功能,请将视图放在 DrawerLayout 内。将整个 <LinearLayout> 封装在 <DrawerLayout> 内。(换句话说,添加 DrawerLayout 作为根视图。)
<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>
  1. 现在,添加抽屉式导航栏,它是一个 NavigationView,使用您刚刚定义的 navdrawer_menu。在 DrawerLayout 中的 </LinearLayout> 元素后面添加以下代码:
<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" />

第 4 步:显示抽屉式导航栏

您已经为抽屉式导航栏和抽屉式导航栏布局创建了菜单项。现在,您需要将抽屉式导航栏连接到导航控制器,以便在用户选择抽屉式导航栏中的项时,应用会导航到相应的 fragment。

  1. 打开 MainActivity.kt Kotlin 文件。在 onCreate() 中,添加可让用户显示抽屉式导航栏的代码。为此,您可以调用 setupWithNavController()。在 onCreate() 的底部添加以下代码:
NavigationUI.setupWithNavController(binding.navView, navController)
  1. 运行应用。从左侧边缘滑动以显示抽屉式导航栏,并确保抽屉式导航栏中的每个菜单项都会使应用转到正确的位置。

虽然抽屉式导航栏可以使用,但您需要再解决一个问题。通常,应用还允许用户通过在主屏幕上点按应用栏中的抽屉式导航栏按钮(三条线)7277f85db3a1ad13.png 来显示抽屉式导航栏。您的应用还未在主屏幕上显示抽屉式导航栏按钮。

第 5 步:通过点按抽屉式导航栏按钮显示抽屉式导航栏

最后一步是让用户能够通过点按应用栏左上角的抽屉式导航栏按钮来访问抽屉式导航栏。

  1. MainActivity.kt Kotlin 文件中,添加 lateinit drawerLayout 成员变量来表示抽屉式导航栏布局:
    private lateinit var drawerLayout: DrawerLayout
  1. onCreate() 方法内,在初始化 binding 变量之后初始化 drawerLayout
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this,
                R.layout.activity_main)

drawerLayout = binding.drawerLayout
  1. drawerLayout 作为第三个参数添加到 setupActionBarWithNavController() 方法:
NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)
  1. 修改 onSupportNavigateUp() 方法以返回 NavigationUI.navigateUp,而不是返回 navController.navigateUp。将导航控制器和抽屉式导航栏布局传递给 navigateUp()。该方法将如下所示:
override fun onSupportNavigateUp(): Boolean {
   val navController = this.findNavController(R.id.myNavHostFragment)
   return NavigationUI.navigateUp(navController, drawerLayout)
}
  1. 您可能需要向文件中添加其他导入,以便解析所有引用,例如:
import androidx.drawerlayout.widget.DrawerLayout
  1. 运行应用。从左侧边缘滑动以显示抽屉式导航栏,并确保抽屉式导航栏中的每个菜单项都会使应用转到正确的位置。
  2. 转到主屏幕,然后点按抽屉式导航栏按钮 7277f85db3a1ad13.png,以确保出现了抽屉式导航栏。确保您在点击抽屉式导航栏中的 RulesAbout 选项后会转到正确的位置。

85fa13d14ad593d4.png

恭喜!

现在,您已经向应用中添加了几个不同的导航选项。

用户现在可以通过玩游戏来逐步在应用中导航。他们可以随时使用向上按钮来返回主屏幕。他们可以通过选项菜单或抽屉式导航栏转到“About”屏幕。用户按返回按钮后,会以一种对应用有意义的方式返回之前的屏幕。用户可以通过在任意屏幕上从左向内滑动或在主屏幕上点按应用栏中的抽屉式导航栏按钮来打开抽屉式导航栏。

您的应用包含强大而有逻辑的导航路径,让用户使用起来感觉很直观。恭喜!

Android Studio 项目:AndroidTriviaNavigation

如需使用 Android 导航库,您需要执行一些设置:

  • 在模块级 build.gradle 文件中为 navigation-fragment-ktxnavigation-ui-ktx 添加依赖项。
  • 在项目级 build.gradle 文件中为 navigationVersion 添加 ext 变量。

导航目的地是指用户导航到的 fragment、activity 或其他应用组件。导航图定义了从一个导航目的地到下一个导航目的地的可能路径。

  • 如需创建导航图,请创建一个类型为 Navigation 的新 Android 资源文件。该文件定义了应用中的导航流。该文件位于 res/navigation 文件夹中,通常名为 navigation.xml
  • 如需在 Navigation Editor 中查看导航图,请打开 navigation.xml 文件,然后点击 Design 标签页。
  • 您可以使用 Navigation Editor 向导航图中添加目的地,如 fragment。
  • 如需定义从一个目的地到另一个目的地的路径,请使用导航图创建连接这两个目的地的操作。在 navigation.xml 文件中,每个连接都表示为具有 IDaction

导航宿主 fragment 通常名为 NavHostFragment,它充当导航图中 fragment 的宿主:

  • 当用户在导航图中定义的目的地之间移动时,NavHostFragment 会换入或换出 fragment 并管理 fragment 返回堆栈。
  • activity_main.xml 布局文件中,NavHostFragment 由名为 android:name="androidx.navigation.fragment.NavHostFragment"fragment 元素表示。

如需定义当用户点按某个视图(例如按钮)时要显示哪个 fragment,请为该视图设置 onClick 监听器:

  • onClick 监听器中,对视图调用 findNavController().navigate()
  • 指定通向目的地的 actionID

条件导航是指在一种情况下导航到一个屏幕,而在另一种情况下导航到另一个屏幕。如需创建条件导航,请执行以下操作:

  1. 使用 Navigation Editor 从起始 fragment 到每个可能的目的地 fragment 创建一个连接。
  2. 为每个连接指定唯一的 ID。
  3. View 的点击监听器方法中,添加相应的代码以检测条件。然后,对视图调用 findNavController().navigate(),并传入相应操作的 ID。

返回按钮

系统的返回按钮通常位于设备的底部。默认情况下,用户按返回按钮后会导航回他们最近查看的屏幕。在某些情况下,您可以控制用户按返回按钮后会转到何处:

  • 在 Navigation Editor 中,您可以使用 Attributes 窗格来更改操作的 popUpTo 设置。此设置会从返回堆栈中移除目的地,其作用是确定用户按返回按钮后会转到何处。
  • navigation.xml 文件中,popUpTo 设置显示为 popUpTo 属性。

1f8e86b02d795270.png

  • 选中 popUpToInclusive 复选框时,会将 popUpToInclusive 属性设置为 true。系统会从返回堆栈中移除直到并包括此目的地在内的所有目的地。
  • 如果操作的 popUpTo 属性设置为应用的起始目的地且 popUpToInclusive 设置为 true,则用户按返回按钮后会直接退出应用。

向上按钮

Android 应用中的屏幕可以有屏幕上的向上按钮,该按钮将显示在应用栏的左上角。(应用栏有时称为操作栏。)向上按钮会根据屏幕之间的层次关系使用户在应用的屏幕中“向上”导航。

导航控制器的 NavigationUI 库与应用栏相集成,让用户能够通过点按应用栏中的向上按钮,从应用中的任意位置返回应用的主屏幕。

如需将导航控制器关联到应用栏,请执行以下操作:

  1. onCreate() 中,对 NavigationUI 类调用 setupActionBarWithNavController(),并传入导航控制器:
val navController = this.findNavController(R.id.myNavHostFragment)
NavigationUI.setupActionBarWithNavController(this,navController)
  1. 替换 onSupportNavigateUp() 方法以在导航控制器中调用 navigateUp()
override fun onSupportNavigateUp(): Boolean {
        val navController = this.findNavController(R.id.myNavHostFragment)
        return navController.navigateUp()
    }
}

选项菜单

选项菜单是指用户从应用栏中点按带有三个垂直点的图标 4cdd17fa43bfbe6.png 来访问的菜单。如需创建一个选项菜单,让其包含的菜单项显示 fragment,请确保 fragment 具有 ID。然后,定义选项菜单,并为菜单项的 onOptionsItemSelected() 处理程序编码。

  1. 确保 fragment 具有 ID:
  • 向导航图中添加目的地 fragment,并注意 fragment 的 ID。(如果愿意,您可以更改 ID。)
  1. 定义选项菜单:
  • 创建一个类型为 Menu 的 Android 资源文件,该文件通常名为 options_menu.xml。该文件存储在 Res > Menu 文件夹中。
  • 在设计编辑器中打开 options_menu.xml 文件,然后将一个 Menu Item 微件从 Palette 窗格拖动到菜单。
  • 为方便起见,请将菜单项的 ID 设为与用户点击此菜单项时要显示的 fragment 的 ID 相同。此步骤不是必需操作,但会使为菜单项的 onClick 行为编码变得更容易。
  1. 为菜单项的 onClick 处理程序编码:
  • 在显示选项菜单的 fragment 或 activity 中,在 onCreateView() 中,调用 setHasOptionsMenu(true) 以启用选项菜单。
  • 实现 onCreateOptionsMenu() 以膨胀选项菜单:
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        super.onCreateOptionsMenu(menu, inflater)
        inflater.inflate(R.menu.options_menu, menu)
}
  • 替换 onOptionsItemSelected() 方法以在用户点击菜单项时采取适当的操作。以下代码显示了与菜单项具有相同 ID 的 fragment。(仅当菜单项和 fragment 具有完全相同的 ID 值时,此代码才有效。)
override fun onOptionsItemSelected(item: MenuItem): Boolean {
     return NavigationUI.
            onNavDestinationSelected(item,requireView().findNavController())
            || super.onOptionsItemSelected(item)
}

抽屉式导航栏

抽屉式导航栏是从屏幕边缘滑出的一个面板。用户可以通过两种方式来打开抽屉式导航栏:

  • 在任意屏幕上从起始边缘(通常是左侧边缘)滑动。
  • 使用应用顶部的应用栏中的抽屉式导航栏按钮(三条线)7277f85db3a1ad13.png

如需向应用中添加抽屉式导航栏,请执行以下操作:

  1. 将依赖项添加到 build.gradle (app)
  2. 确保每个目的地 fragment 都有一个 ID。
  3. 创建抽屉式导航栏的菜单。
  4. 将抽屉式导航栏添加到 fragment 的布局。
  5. 将抽屉式导航栏连接到导航控制器。
  6. 设置应用栏中的抽屉式导航栏按钮。

下面更为详细地介绍了这些步骤。

  1. 将依赖项添加到 build.gradle
  • 抽屉式导航栏是 Material Components for Android 库的一部分。将 Material 库添加到 build.gradle (app) 文件:
dependencies {
    ...
    implementation "com.google.android.material:material:$supportlibVersion"
    ...
}
  1. 为每个目的地 fragment 指定一个 ID:
  • 如果可以从抽屉式导航栏到达某个 fragment,请在导航图中将其打开,以确保它具有一个 ID。
  1. 创建抽屉式导航栏的菜单:
  • 为抽屉式导航栏菜单创建一个类型为 Menu(通常名为 navdrawer_menu)的 Android 资源文件。这样会在 Res > Menu 文件夹中创建一个新的 navdrawer_menu.xml 文件。
  • 在设计编辑器中,将 Menu Item 微件添加到 Menu
  1. 将抽屉式导航栏添加到 fragment 的布局:
  • 在包含导航宿主 fragment 的布局(它通常是主布局)中,使用 <androidx.drawerlayout.widget.DrawerLayout> 作为根视图。
  • 将一个 <com.google.android.material.navigation.NavigationView> 视图添加到布局中。
  1. 将抽屉式导航栏连接到导航控制器:
  • 打开创建导航控制器的 activity。(主 activity 通常是您在此处想要的 activity。)在 onCreate() 中,使用 NavigationUI.setupWithNavController() 将抽屉式导航栏与导航控制器连接起来:
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(
       this, R.layout.activity_main)
NavigationUI.setupWithNavController(binding.navView, navController)
  1. 设置应用栏中的抽屉式导航栏按钮:
  • 在创建导航控制器的 activity(它通常是主 activity)的 onCreate() 中,将抽屉式导航栏布局作为第三个参数传递给 NavigationUI.setupActionBarWithNavController
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(
    this, R.layout.activity_main)

NavigationUI.setupActionBarWithNavController(
    this, navController, binding.drawerLayout)
  • 如需将向上按钮与抽屉式导航栏按钮一起使用,请修改 onSupportNavigateUp() 以返回 NavigationUI.navigateUp()。将导航控制器和抽屉式导航栏布局传递给 navigateUp()
override fun onSupportNavigateUp(): Boolean {
   val navController = this.findNavController(R.id.myNavHostFragment)
   return NavigationUI.navigateUp(navController, drawerLayout)
}

Udacity 课程:

Android 开发者文档:

Material Design 文档:

此部分列出了在由讲师主导的课程中,学生学习此 Codelab 后可能需要完成的家庭作业。讲师自行决定是否执行以下操作:

  • 根据需要布置作业。
  • 告知学生如何提交家庭作业。
  • 给家庭作业评分。

讲师可以酌情采纳这些建议,并且可以自由布置自己认为合适的任何其他家庭作业。

如果您是在自学此 Codelab,可随时通过这些家庭作业来检测您的知识掌握情况。

构建并运行应用

TitleFragment 的布局中添加 Rules 按钮和 About 按钮,如下所示。当用户点按 RulesAbout 按钮时,应用会视情况导航到 RulesFragmentAboutFragment

ed9821f0f17745bc.png

额外的学分:

RulesFragmentAboutFragment 的每个布局中,添加一个可使应用导航到 GameFragmentPlay 按钮,如下所示:

ebb54587c3a10afb.png dbfcc16f3e3f30b9.png

提示:您需要更新这两个 fragment 的布局以启用数据绑定。

更改返回堆栈以启用以下用户流:

  1. 用户点按 Play 按钮以从 RulesFragment 屏幕或 AboutFragment 屏幕转到 GameFragment
  2. 然后,用户点按屏幕底部的返回按钮。
  3. 应用会导航回 TitleFragment,而不是 RulesFragmentAboutFragment

换句话说,如果用户在查看有关游戏的规则或信息时点按 Play 按钮,然后点按返回按钮,系统会从返回堆栈中移除 RulesFragmentAboutFragment

回答以下问题

问题 1

如何使项目能够使用导航组件?

  • 确保每个 Activity 类都会扩展 NavigationActivity 类。
  • NavigationController 类用作启动 activity。
  • <uses-navigation> 添加到 Android 清单文件中。
  • build.gradle (module) 文件中添加 navigation-fragment-ktxnavigation-ui-ktx 的依赖项。

问题 2

应用中可能的导航路径是在何处定义的?

  • res > layout 文件夹内的文件(通常名为 navigation.xml))中。
  • app > navigation 文件夹内的文件(通常名为 navigation.xml))中。
  • res > navigation 文件夹内的文件(通常名为 navigation.xml))中。
  • android-manifest.xml 文件的 <navigation> 元素中。

问题 3

以下关于 NavHostFragment 的表述中哪些是正确的?请选择所有适用的选项。

  • 当用户在导航图中定义的目的地之间移动时,NavHostFragment 会根据需要换入或换出 fragment。
  • 您可以在“Project”视图中点击 NavHostFragment 来打开导航图。
  • 您可以通过添加一个名为 androidx.navigation.fragment.NavHostFragment<fragment> 来将 NavHostFragment 添加到主布局中。
  • 您必须创建一个 NavHostFragment 子类,并实现 onNavigate() 方法来处理不同类型的导航(如按钮点击)。

问题 4

以下关于导航图的表述中哪些是正确的?请选择所有适用的选项。

  • 您可以通过连接导航图中的 fragment 在应用中创建从一个 fragment 到另一个 fragment 的潜在路径。
  • fragment 之间的连接的 typeAction
  • 您必须向包含在导航图中的每个 <fragment> 添加 type="navigation" 属性。
  • 如需打开导航图,您可以在 Android Studio 的“Project”窗格中双击导航文件 (navigation.xml),然后点击 Design 标签页。

问题 5

在何处设置要在导航中使用的 fragment 的 ID?

  • 在 fragment 的布局文件中,在设计编辑器中或在 res > layout 文件夹内的布局 XML 文件中设置 ID 属性。
  • 在项目的导航文件中,在导航图中或在 res > navigation 文件夹内的导航 XML 文件中设置 ID 属性。
  • 您需要同时在应用的导航文件中和 fragment 的布局文件中设置 ID。
  • 在相关的 Fragment 类中设置 ID 变量。

问题 6

新闻应用具有一个显示 Show headlines 按钮的 NewsFragment。目标是当用户点击此按钮时,让应用导航到 HeadlinesFragment

假设您已在导航图中添加从 NewsFragmentHeadlinesFragment 的连接,如下所示:

801ce57177a44b96.png

为了在用户点按 Show headlines 按钮时让应用导航到 HeadlinesFragment,您还需要执行什么操作?

  • 实际上,您无需执行其他任何操作。在导航图中设置导航路径就足够了。
  • Show headlines 按钮的 onclickListener 中,对导航控制器调用 navigate(),并传递目的地 fragment 的类名称(在本例中为 HeadlinesFragment)。
  • Show headlines 按钮的 onclickListener 中,对导航控制器调用 navigate(),并传递将 NewsFragment 连接到 HeadlinesFragment 的操作。
  • Show headlines 按钮的 onclickListener 中,对容器 fragment 调用 navigateTo(),并传递目的地 fragment 的类名称(在本例中为 HeadlinesFragment)。

问题 7

当用户在应用中导航时,有时他们希望沿原路返回已访问过的屏幕。

假设存在以下情况:

  • fragmentA 通过 action_FragmentA_to_FragmentB 连接到 fragmentB
  • fragmentB 通过 action_FragmentB_to_FragmentC 连接到 fragmentC

849190b4b57aa001.png

以下关于在应用中向前和向后导航的表述中哪些是正确的?(请选择所有适用的选项。)

  • action_FragmentA_to_FragmentB 操作的目的地指定当用户位于 FragmentA 时,应用中的下一个目的地是 FragmentB
  • action_FragmentA_to_FragmentB 操作的目的地设置用户转到的下一个目的地,无论用户是点按应用中的按钮,还是点按屏幕底部的返回按钮。
  • 操作的 popUpTo 属性可以修改当用户点按系统返回按钮时应用导航到的位置。
  • 操作的 popUpTo 属性可以修改当用户在应用中向前导航时接下来要转到的位置。

问题 8

当用户在应用中导航时,有时他们希望沿原路返回已访问过的屏幕。不过,您可以使用操作的 popUpTopopUpToInclusive 属性来修改在应用中向后导航的路径。

假设存在以下情况:

  • fragmentA 通过 action_FragmentA_to_FragmentB 连接到 fragmentB
  • fragmentB 通过 action_FragmentB_to_FragmentC 连接到 fragmentC

6cb8e9f498205344.png

用户从 fragmentA 导航到 fragmentB 再到 fragmentC,然后点按系统返回按钮。在这种情况下,假设您希望应用导航回 fragmentA(而不是 fragmentB)。设置 popUpTopopUpToInclusive 属性的正确方法是什么?

  • 对于 action_FragmentA_to_FragmentB,将 popUpTo 设置为 fragmentB,不为 popUpToInclusive 设置任何值。对于 action_FragmentB_to_FragmentC,将 popUpTo 设置为 fragmentA,将 popUpToInclusive 设置为 true
  • 对于 action_FragmentA_to_FragmentB,将 popUpTo 设置为 fragmentA,将 popUpToInclusive 设置为 true。对于 action_FragmentB_to_FragmentC,将 popUpTo 设置为 fragmentA,将 popUpToInclusive 设置为 true
  • 对于 action_FragmentA_to_FragmentB,将 popUpTo 设置为“none”,不为 popUpToInclusive 设置任何值。(您可以省略这两个属性。)对于 action_FragmentB_to_FragmentC,将 popUpTo 设置为 fragmentA,将 popUpToInclusive 设置为 true
  • 对于 action_FragmentA_to_FragmentB,将 popUpTo 设置为“none”,不为 popUpToInclusive 设置任何值。(您可以省略这两个属性。)对于 action_FragmentB_to_FragmentC,将 popUpTo 设置为 fragmentA,将 popUpToInclusive 设置为 false.

问题 9

假设目的地图中 action_headlinesFragment_to_newsArticle 操作的 popUpTo 值为 newsFragment

8857c4801f7483cd.png

假设用户打开应用,并按以下顺序在屏幕之间导航(未使用返回按钮):

打开应用,依次转到“News”主屏幕 >“Headlines”>“News details”

当用户查看 News details 屏幕时,如果他们点按屏幕底部的系统返回按钮,会发生什么情况?

请选择所有适用的选项(注意,popUpTonewsFragment)。

  • 如果 popUpToInclusivetrue:Android 会退出应用,因为此应用的返回堆栈中没有剩下任何内容。
  • 如果 popUpToInclusivefalse:应用会返回“News”主屏幕。
  • 如果 popUpToInclusivetrue:应用会返回“News”主屏幕。
  • 如果 popUpToInclusivefalse:应用会返回“Headlines”屏幕。

问题 10

在什么位置定义菜单项?

  • 这取决于菜单的显示位置。对于抽屉式导航栏菜单,在 res > drawer 文件夹内的 menu.xml 文件中为每个菜单项添加一个 <item> 标记。对于选项菜单,在 res > options 文件夹内的 menu.xml 文件中为每个菜单项添加一个 <item> 标记。
  • 在显示菜单的 fragment 或 activity 的布局文件中,添加一个 <menu> 标记,其中包含每个菜单项的 <item> 标记。
  • res > menu 文件夹内的 menu_name.xml 文件中,为每个菜单项添加一个 <item> 标记。为每个单独的菜单创建单独的 XML 文件。
  • android_manifest.xml 文件中,添加一个 <menus> 标记,其中包含每个菜单的 <menu> 标记。对于每个 <menu> 标记,为每个菜单项添加一个 <item> 标记。

问题 11

如需在应用中启用选项菜单,您需要定义菜单项。然后,您需要在要显示选项菜单的 ActivityFragment 中执行什么操作?

请选择所有适用的选项:

  • 对于 activity,在 onCreate() 中调用 setHasOptionsMenu(true);对于 fragment,在 onCreateView() 中调用该方法。
  • 在 activity 或 fragment 中实现 onCreateOptionsMenu() 以创建菜单。
  • 在菜单的 XML 文件中将 onClick 属性设置为 onShowOptionsMenu,除非您为选项菜单实现自定义 onClick 监听器,在这种情况下,应指定自定义 onClick 监听器的名称。
  • 在 activity 或 fragment 中实现 onOptionsItemSelected(),以确定当用户选择选项菜单中的菜单项时会发生什么情况。

问题 12

如需在应用中启用抽屉式导航栏,您需要执行什么操作?您可以假设您的项目正在使用导航图,并且您已定义菜单项。

请选择所有适用的选项:

  • 在相关的布局文件中将 <DrawerLayout> 用作根视图,并向该布局添加一个 <NavigationView> 标记。
  • 在相关的布局文件中将 <Navigation> 用作根视图,并向该布局添加一个 <NavigationView> 标记。
  • 在布局的 <NavigationView> 中,将 android:menu 属性设置为抽屉式导航栏菜单。
  • 在导航 XML 文件中,确保导航菜单有一个 ID。

问题 13

接着上一个问题来说,您需要编写一些代码,以启用在用户从屏幕左侧向内滑动时要显示的抽屉式导航栏。

在创建导航控制器的 Activity 内的 onCreate() 中,要添加的正确代码是什么?

  • A:
NavigationUI.setupWithNavController(
    navigationLayoutID, navigationMenuID)
  • B:
NavigationUI.setupWithNavController(
    navigationView, navigationController)
  • C:
NavigationDrawer.setupWithNavInterface(
    navigationView, navigationController)
  • D:
NavigationDrawer.setupWithNavController(
    navigationView, navigationMenuID)

问题 14

如何添加对位于屏幕顶部的向上按钮的支持以使用户能够从应用中的任意位置返回应用的主屏幕?您需要在相关的 Activity 中执行什么操作?

请选择所有适用的选项:

  • res > menu 文件夹中,创建 up_menu.xml 文件。
  • 使用 NavigationUI.setupActionBarWithNavController(context,navigationController) 将导航控制器关联到应用栏。
  • 替换 onSupportNavigateUp() 方法以对导航控制器调用 navigateUp()
  • 在导航图中,确保起始 fragment 的 IDHomeFragment

提交应用以进行评分

评分者的评分指南

检查应用是否具有以下功能:

  • TitleFragment 屏幕具有 RulesAbout 按钮,如下所示。
  • 当用户点按 RulesAbout 按钮时,应用会视情况导航到 RulesFragmentAboutFragment

ed9821f0f17745bc.png

额外的学分:

RulesFragmentAboutFragment 屏幕都有会使应用导航到 GameFragmentPlay 按钮,如下所示:

ebb54587c3a10afb.png dbfcc16f3e3f30b9.png

检查这两个 fragment 的布局是否都启用了数据绑定。

检查以下用户流:

  1. 点按 Play 按钮以从规则 fragment 屏幕或关于 fragment 屏幕转到游戏 fragment。
  2. 按屏幕底部的系统返回按钮。

应用应导航回标题 fragment,而不是规则 fragment 或关于 fragment。

开始学习下一课:启动外部 activity

如需本课程中其他 Codelab 的链接,请查看“Android Kotlin 基础知识”Codelab 着陆页