学习 Jetpack 导航

1. 开始之前

导航架构组件Navigation Architecture Component简化了导航的实现,同时也有助于您可视化应用程序的导航流程。该库提供了许多好处,包括

  • 自动处理片段事务
  • 默认情况下正确处理向上返回
  • 动画和过渡的默认行为
  • 将深度链接作为一等操作
  • 只需少量额外工作即可实现导航 UI 模式(如导航抽屉和底部导航**)**
  • 导航时传递信息时的类型安全
  • Android Studio 工具,用于可视化和编辑应用程序的导航流程

您将做什么

在这个 codelab 中,您将使用下面所示的示例应用程序

6884c79d552f943.gif

所有活动和片段都已为您创建。您将使用导航组件连接它们,并在这样做的过程中实现以下内容:

  • 可视化导航图
  • 按目的地和操作导航
  • 过渡动画
  • 菜单导航、底部导航和菜单抽屉导航
  • 类型安全的参数传递
  • 深度链接

先决条件

  • Kotlin 基础知识(此 codelab 使用 Kotlin)
  • 最新稳定版本的Android Studio以及如何使用它的知识
  • 运行 API 19+ 的模拟器或设备

2. 开始

获取代码

从 GitHub 克隆导航 codelab

$ git clone https://github.com/android/codelab-android-navigation

或者,您可以将存储库下载为 Zip 文件

获取最新稳定版本的 Android Studio

确保您使用的是最新版本的 Android Studio。Android Studio 导航工具需要此版本。

如果您需要下载最新版本的 Android Studio,可以点击此处

导航概述

导航组件由三个关键部分组成,它们和谐地协同工作。它们是:

  1. 导航图(新的 XML 资源) - 这是一种资源,它在一个集中位置包含所有与导航相关的的信息。这包括应用程序中的所有位置(称为目的地)以及用户可能在应用程序中采取的所有路径。
  2. NavHostFragment(布局 XML 视图) - 这是添加到布局中的特殊小部件。它显示导航图中的不同目的地。
  3. NavController(Kotlin/Java 对象) - 此对象跟踪导航图中的当前位置。当您在导航图中移动时,它会协调在NavHostFragment中交换目的地内容。

导航时,您将使用 NavController 对象,告诉它您想要去哪里或要在导航图中采取什么路径。NavController然后将在 NavHostFragment 中显示相应的目的地。

这就是基本思想。让我们看看在实践中它是什么样的,从新的导航图资源开始。

3. 介绍导航图

目的地

导航组件引入了目的地的概念。目的地是您可以在应用程序中导航到的任何位置,通常是片段或活动。这些可以直接使用,但您也可以创建您自己的自定义目的地类型(如果需要)。

导航图是一种新的资源类型,它定义了用户可以在应用程序中采取的所有可能路径。它以可视方式显示可以从给定目的地到达的所有目的地。Android Studio 在其导航编辑器中显示该图。这是您将为应用程序创建的起始导航图的一部分

Home, Step One and Step Two

探索导航编辑器

  1. 打开res/navigation/mobile_navigation.xml
  2. 点击设计进入设计模式

f01c48a365da45fc.png

您应该看到以下内容:

92fe16a524095729.png

导航图显示可用的目的地。目的地之间的箭头称为操作。稍后您将了解有关操作的更多信息。

  1. 点击目的地以查看其属性。

4d58e392ee13a9ba.png

  1. 点击任何操作(用箭头表示)以查看其属性。

903fa5df3721baf9.png

导航 XML 文件的结构

您在图形导航编辑器中进行的所有更改都会更改底层的 XML 文件,这类似于布局编辑器修改布局 XML 的方式。

点击文本选项卡

d5f69532f6f303f6.png

您将看到类似以下内容的 XML 代码:

<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"
    app:startDestination="@+id/home_dest">

    <!-- ...tags for fragments and activities here -->

</navigation>

注意:

  • <navigation>是每个导航图的根节点。
  • <navigation>包含一个或多个目的地,由<activity><fragment>元素表示。
  • app:startDestination是一个属性,它指定用户首次打开应用程序时启动的目的地。

让我们来看一下片段目的地

<fragment
    android:id="@+id/flow_step_one_dest"
    android:name="com.example.android.codelabs.navigation.FlowStepFragment"
    tools:layout="@layout/flow_step_one_fragment">
    <argument
        .../>

    <action
        android:id="@+id/next_action"
        app:destination="@+id/flow_step_two_dest">
    </action>
</fragment>

注意:

  • android:id定义片段的 ID,您可以使用它在此 XML 和代码中的其他地方引用该目的地。
  • android:name声明在导航到该目的地时要实例化的片段的完全限定类名。
  • tools:layout指定在图形编辑器中应显示哪个布局。

一些<fragment>标签还包含<action><argument><deepLink>,我们将在稍后介绍所有这些内容。

4. 向导航图添加目的地

示例应用程序在图中以一些目的地开始。在此步骤中,您将添加一个全新的目的地。您必须先将目的地添加到导航图,然后才能导航到它。

  1. 打开res/navigation/mobile_navigation.xml,然后点击设计选项卡。
  2. 点击新建目的地图标,然后选择“settings_fragment”

44a8212036929458.png

结果是一个新的目的地,它在设计视图中呈现片段布局的预览。

e82a44431b77c6c1.png

请注意,您也可以直接编辑 XML 文件来添加目的地

mobile_navigation.xml

<fragment
    android:id="@+id/settings_dest"
    android:name="com.example.android.codelabs.navigation.SettingsFragment"
    android:label="@string/settings"
    tools:layout="@layout/settings_fragment" />

5. 使用导航图进行导航

现在您有了这个很棒的导航图,但您实际上并没有使用它来导航。

活动和导航

导航组件遵循导航原则中概述的指导。导航原则建议您将活动用作应用程序的入口点。活动还将包含全局导航,例如底部导航,

相比之下,片段将是实际的特定于目的地的布局。

为了使所有这些都能工作,您需要修改您的活动布局以包含一个称为NavHostFragment的特殊小部件。NavHostFragment会在您浏览导航图时进出交换不同的片段目的地。

b85ea429c5ebad71.png

支持导航的简单布局(类似于上图)如下所示。此代码示例可在res/layout-470dp/navigation_activity.xml中找到

<LinearLayout
    .../>
    <androidx.appcompat.widget.Toolbar
        .../>
    <fragment
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:id="@+id/my_nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:navGraph="@navigation/mobile_navigation"
        app:defaultNavHost="true"
        />
    <com.google.android.material.bottomnavigation.BottomNavigationView
        .../>
</LinearLayout>

注意:

  • 这是一个活动布局。它包含全局导航,包括底部导航和工具栏
  • android:name="androidx.navigation.fragment.NavHostFragment"app:defaultNavHost="true"将系统后退按钮连接到NavHostFragment
  • app:navGraph="@navigation/mobile_navigation"NavHostFragment与导航图关联。此导航图指定用户可以在此NavHostFragment中导航到的所有目的地。

最后,当用户执行点击按钮之类的操作时,您需要触发导航命令。一个名为NavController的特殊类是触发NavHostFragment中片段交换的类。

// Command to navigate to flow_step_one_dest
findNavController().navigate(R.id.flow_step_one_dest)

请注意,您可以传递目的地或操作 ID 来进行导航。这些是在导航图 XML 中定义的 ID。这是一个传递目的地 ID 的示例。

NavController功能强大,因为当您调用navigate()popBackStack()之类的方法时,它会根据您导航到或来自的目的地类型将这些命令转换为相应的框架操作。例如,当您使用活动目的地调用navigate()时,NavController会代表您调用startActivity()

有几种方法可以获取与您的NavHostFragment关联的NavController对象。在 Kotlin 中,建议您使用以下扩展函数之一,具体取决于您是在片段、活动还是视图中调用导航命令

轮到您使用NavController进行导航。您将连接导航到目的地按钮以导航到flow_step_one_dest目的地(这是一个FlowStepFragment目的地)

  1. 打开HomeFragment.kt
  2. onViewCreated()中连接navigate_destination_button

HomeFragment.kt

val button = view.findViewById<Button>(R.id.navigate_destination_button)
button?.setOnClickListener {
    findNavController().navigate(R.id.flow_step_one_dest, null)
}
  1. 运行应用程序并点击**导航到目的地**按钮。请注意,此按钮会导航到flow_step_one_dest目的地。

点击监听器代码如下所示

val button = view.findViewById<Button>(R.id.navigate_destination_button)
button?.setOnClickListener(
        Navigation.createNavigateOnClickListener(R.id.flow_step_one_dest, null)
)

6. 更改导航转换

每个navigate()调用都带有一个不太令人兴奋的默认转换,如下所示

From Home user clicks on Navigation to destination and navigates to step one.

可以通过包含一组NavOptions来覆盖默认转换以及与调用关联的其他属性。NavOptions使用Builder模式,允许您仅覆盖和设置所需的选项。还有一个用于NavOptions的ktx DSL,这就是您将要使用的。

对于动画转换,您可以在anim资源文件夹中定义XML动画资源,然后将这些动画用于转换。应用程序代码中包含一些示例

409164bbcdea93ed.png

添加自定义转换

更新代码,以便按下**导航到目的地**按钮显示自定义转换动画。

  1. 打开HomeFragment.kt
  2. 定义NavOptions并将其传递到对navigate_destination_buttonnavigate()调用中
val options = navOptions {
    anim {
        enter = R.anim.slide_in_right
        exit = R.anim.slide_out_left
        popEnter = R.anim.slide_in_left
        popExit = R.anim.slide_out_right
    }
}
view.findViewById<Button>(R.id.navigate_destination_button)?.setOnClickListener {
    findNavController().navigate(R.id.flow_step_one_dest, null, options)
}
  1. 如果步骤5中添加的代码仍然存在,请将其删除
  2. 验证点击**导航到目的地**按钮是否会导致片段滑入屏幕,以及按下返回键是否会导致其滑出屏幕

dcb9d8241c6647eb.gif

7. 使用操作导航

操作

导航系统还允许您通过*操作*进行导航。如前所述,导航图中显示的行是操作的可视化表示。

bc86b406d6a75be0.png

通过操作进行导航与通过目的地进行导航相比具有以下优势

  • 您可以可视化应用程序中的导航路径
  • 操作可以包含您可以设置的其他关联属性,例如转换动画、参数值和回退堆栈行为
  • 您可以使用safe args插件进行导航,您很快就会看到

以下是连接flow_step_one_destflow_step_two_dest的操作的可视化和XML

2ab28ff13c759c17.png

<fragment
    android:id="@+id/flow_step_one_dest"
    android:name="com.example.android.codelabs.navigation.FlowStepFragment">

    <argument
        .../>

    <action
        android:id="@+id/next_action"
        app:destination="@+id/flow_step_two_dest">
    </action>
</fragment>

<fragment
    android:id="@+id/flow_step_two_dest"
    android:name="com.example.android.codelabs.navigation.FlowStepFragment">
    <!-- ...removed for simplicity-->
</fragment>

注意:

  • 操作嵌套在目的地中 - 这是您将从中导航的目的地
  • 操作包含一个引用flow_step_two_dest的目的地参数;这是您将导航到的ID
  • 操作的ID是“next_action”

这是另一个示例,连接flow_step_two_desthome_dest的操作

6d4a5b6f629c3910.png

<fragment
    android:id="@+id/home_dest"
    android:name="com.example.android.codelabs.navigation.HomeFragment"
    .../>

<fragment
    android:id="@+id/flow_step_two_dest"
    android:name="com.example.android.codelabs.navigation.FlowStepFragment">

    <argument
        .../>

    <action
        android:id="@+id/next_action"
        app:popUpTo="@id/home_dest">
    </action>
</fragment>

注意:

  • 连接flow_step_two_desthome_dest的操作使用相同的ID next_action。您可以使用来自flow_step_one_destflow_step_two_dest的next_action id进行导航。这是一个示例,说明操作如何提供抽象级别,并可以根据上下文将您导航到不同的位置。
  • 使用了popUpTo属性 - 此操作将从回退堆栈中弹出片段,直到您到达home_dest

是时候连接**使用操作导航**按钮了,让它名副其实!

  1. 在**设计**模式下打开mobile_navigation.xml文件
  2. home_dest拖动箭头到flow_step_one_dest

353eea1044f6d048.png

  1. 选中操作箭头(蓝色)后,更改操作的属性,以便
  • ID = next_action
  • 进入转换 = slide_in_right
  • 退出转换 = slide_out_left
  • 弹出进入转换 = slide_in_left
  • 弹出退出转换 = slide_out_right

9670657fcc5a8de0.png

  1. 点击文本选项卡

请注意home_dest目的地下新添加的next_action操作

mobile_navigation.xml

<fragment android:id="@+id/home_dest"
        ...>
        
        <action android:id="@+id/next_action"
            app:destination="@+id/flow_step_one"
            app:enterAnim="@anim/slide_in_right"
            app:exitAnim="@anim/slide_out_left"
            app:popEnterAnim="@anim/slide_in_left"
            app:popExitAnim="@anim/slide_out_right" />
  1. 打开HomeFragment.kt
  2. navigate_action_button添加点击监听器

HomeFragment.kt

 view.findViewById<Button>(R.id.navigate_action_button)?.setOnClickListener(
        Navigation.createNavigateOnClickListener(R.id.next_action, null)
)
  1. 验证点击**使用操作导航**是否现在导航到下一个屏幕。

8. 使用safe args进行导航

Safe Args

导航组件有一个Gradle插件,称为safe args,它为对为目的地和操作指定的参数进行类型安全访问生成简单的对象和构建器类。

在目的地之间传递值时,safe args允许您摆脱如下所示的代码

val username = arguments?.getString("usernameKey")

并将其替换为具有生成的setter和getter的代码。

val username = args.username

使用safe args传递值

  1. 打开项目build.gradle文件,并注意safe args插件

build.gradle

dependencies {
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
    //...
    }

  1. 打开app/build.gradle文件,并注意应用的插件

app/build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'androidx.navigation.safeargs.kotlin'

android { 
   //...
}
  1. 打开mobile_navigation.xml, 并注意如何在flow_step_one_dest目的地中定义参数。

mobile_navigation.xml

<fragment
    android:id="@+id/flow_step_one_dest"
    android:name="com.example.android.codelabs.navigation.FlowStepFragment"
    tools:layout="@layout/flow_step_one_fragment">
    <argument
        android:name="flowStepNumber"
        app:argType="integer"
        android:defaultValue="1"/>

    <action...>
    </action>
</fragment>

使用<argument>标签,safeargs会生成一个名为FlowStepFragmentArgs的类。

d0e308373c2656d9.png

由于XML包含一个名为flowStepNumber的参数,由android:name="flowStepNumber"指定,因此生成的类FlowStepFragmentArgs将包含一个具有getter和setter的变量flowStepNumber

36d12ab963cb6ca6.png

  1. 打开FlowStepFragment.kt
  2. 注释掉下面显示的代码行

FlowStepFragment.kt

// Comment out this line
// val flowStepNumber = arguments?.getInt("flowStepNumber")

这种旧式代码不是类型安全的。最好使用safe args。

  1. 更新FlowStepFragment以使用生成的类FlowStepFragmentArgs。这将以类型安全的方式获取FlowStepFragment参数

FlowStepFragment.kt

val safeArgs: FlowStepFragmentArgs by navArgs()
val flowStepNumber = safeArgs.flowStepNumber

Safe Args Direction 类

您还可以使用safe args以类型安全的方式进行导航,无论是否添加参数。您可以使用生成的Directions类来执行此操作。

d0e308373c2656d9.png

为每个具有操作的不同目的地生成Directions类。Directions类包含目的地拥有的每个操作的方法。

例如,HomeFragment.kt中的navigate_action_button点击监听器可以更改为

HomeFragment.kt

// Note the usage of curly braces since we are defining the click listener lambda
view.findViewById<Button>(R.id.navigate_action_button)?.setOnClickListener {
    val flowStepNumberArg = 1
    val action = HomeFragmentDirections.nextAction(flowStepNumberArg)
    findNavController().navigate(action)
}

HomeFragmentDirections.nextAction(flowStepNumberArg)

9. 使用菜单、抽屉和底部导航进行导航

导航组件包含一个NavigationUI类和navigation-ui-ktx Kotlin扩展。 NavigationUI具有将菜单项与导航目的地关联的静态方法,而navigation-ui-ktx是一组执行相同操作的扩展函数。如果NavigationUI找到与当前图上的目的地ID相同的菜单项,则它会将菜单项配置为导航到该目的地。

将NavigationUI与选项菜单一起使用

使用NavigationUI最简单的方法之一是让它简化选项菜单设置。特别是,NavigationUI简化了处理onOptionsItemSelected回调。

  1. 打开MainActivity.kt

注意您已经在onCreateOptionsMenu中有了用于膨胀菜单overflow_menu的代码

  1. 打开res/menu/overflow_menu.xml
  2. 更新您的溢出菜单以包含**settings_dest**

overflow_menu.xml

<item
    android:id="@+id/settings_dest"
    android:icon="@drawable/ic_settings"
    android:menuCategory="secondary"
    android:title="@string/settings" />
  1. 打开MainActivity.kt
  2. 让NavigationUI使用onNavDestinationSelected辅助方法处理onOptionsItemSelected。如果菜单项不打算导航,则使用super.onOptionsItemSelected进行处理

MainActivity.kt

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    return item.onNavDestinationSelected(findNavController(R.id.my_nav_host_fragment))
            || super.onOptionsItemSelected(item)
}
  1. 运行您的应用程序。您应该有一个功能齐全的ActionBar菜单,可以导航到SettingsFragment。

30a096187fb852fe.png

使用NavigationUI配置底部导航

代码已经包含了实现底部导航的XML布局代码,这就是为什么您会看到底部导航栏。但它不会导航到任何地方。

  1. 打开res/layout/navigation_activity/navigation_activity.xml (h470dp)并点击**文本**选项卡

注意底部导航的XML布局代码是如何存在的,并且引用了bottom_nav_menu.xml

<com.google.android.material.bottomnavigation.BottomNavigationView
    android:id="@+id/bottom_nav_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:menu="@menu/bottom_nav_menu" />
  1. 打开res/menu/bottom_nav_menu.xml

注意底部导航有两个项目,并且**它们的id与**导航图目的地的id**匹配**

bottom_nav_menu.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@id/home_dest"
        android:icon="@drawable/ic_home"
        android:title="@string/home" />
    <item
        android:id="@id/deeplink_dest"
        android:icon="@drawable/ic_android"
        android:title="@string/deeplink" />
</menu>

让我们使用NavigationUI让底部导航真正发挥作用。

  1. 打开MainActivity.kt
  2. 使用setupWithNavController(bottomNavigationView: BottomNavigationView, navController: NavController)实现setupBottomNavMenu方法

MainActivity.kt

private fun setupBottomNavMenu(navController: NavController) {
    val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav_view)
    bottomNav?.setupWithNavController(navController)
}

现在您的底部导航可以工作了!

使用NavigationUI配置导航抽屉

最后,让我们使用NavigationUI配置侧边导航和导航抽屉,包括处理ActionBar和正确的向上导航。如果您屏幕足够大,或者屏幕太短而无法容纳底部导航,您将看到这一点。

首先观察正确的布局XML代码已经在应用程序中了。

  1. 打开navigation_activity.xmlnavigation_activity.xml (w960dp)

注意这两个布局都包含一个连接到nav_drawer_menu的NavigationView。在平板电脑版本(w960dp)中,NavigationView始终显示在屏幕上。在较小的设备上,NavigationView嵌套在一个DrawerLayout中

现在开始实现NavigationView导航。

  1. 打开MainActivity.kt
  2. 使用setupWithNavController(navigationView: NavigationView, navController: NavController)实现setupNavigationMenu方法。注意此方法版本采用NavigationView而不是BottomNavigationView

MainActivity.kt

private fun setupNavigationMenu(navController: NavController) {
    val sideNavView = findViewById<NavigationView>(R.id.nav_view)
    sideNavView?.setupWithNavController(navController)
}

现在导航视图菜单将显示在屏幕上,但它不会影响ActionBar。

设置ActionBar需要创建一个AppBarConfiguration的实例。AppBarConfiguration的目的是指定您希望为工具栏、折叠工具栏和操作栏配置的选项。配置选项包括栏是否必须处理抽屉布局以及哪些目标被认为是**顶级目标**。

顶级目标是应用程序的根级目标。这些目标不会在应用栏中显示“向上”按钮,如果目标使用抽屉布局,则会显示抽屉图标。

6cd3eaaf843ff226.png

  1. 通过传入一组顶级目标 ID 和抽屉布局来创建一个AppBarConfiguration

MainActivity.kt

val drawerLayout : DrawerLayout? = findViewById(R.id.drawer_layout)
appBarConfiguration = AppBarConfiguration(
        setOf(R.id.home_dest, R.id.deeplink_dest),
        drawerLayout)

现在您有了AppBarConfiguration,您可以调用NavigationUI.setupActionBarWithNavController。这将执行以下操作:

  • 根据目标的标签在ActionBar中显示标题
  • 当您**不在**顶级目标上时显示向上按钮
  • 当您位于顶级目标上时显示抽屉图标(汉堡图标)
  1. 实现setupActionBarWithNavController

MainActivity.kt

private fun setupActionBar(navController: NavController,
                           appBarConfig : AppBarConfiguration) {
    setupActionBarWithNavController(navController, appBarConfig)
}

您还应该让NavigationUI处理按下向上按钮时发生的情况。

  1. 覆盖onSupportNavigationUp并调用NavigationUI.navigateUp,使用相同的AppBarConfiguration

MainActivity.kt

override fun onSupportNavigateUp(): Boolean {
    return findNavController(R.id.my_nav_host_fragment).navigateUp(appBarConfiguration)
}
  1. 运行您的代码。如果您在分屏模式下打开应用程序,则应该有一个可用的导航抽屉。向上图标和抽屉图标应该在适当的时间显示并正常工作。

d5474187c1c710cc.gif

NavigationView添加新的目标很容易。一旦您使用向上和向后导航使导航抽屉正常工作,您只需要添加新的菜单项即可。

  1. 打开menu/nav_drawer_menu.xml
  2. settings_dest添加一个新的菜单项
<item
    android:id="@+id/settings_dest"
    android:icon="@drawable/ic_settings"
    android:title="@string/settings" />

现在您的导航抽屉将设置屏幕显示为目标。干得好!

b9c243170688706.png

10. 深度链接到目标

导航组件还包括深度链接支持。深度链接是一种跳转到应用程序导航中间的方法,无论是来自实际的URL链接,还是来自通知的挂起意图。

使用导航库处理深度链接的一个好处是,它确保用户从其他入口点(例如应用程序小部件、通知或网络链接(在下一步中介绍))开始时,位于正确的目标并具有相应的返回堆栈。

Navigation 提供了一个NavDeepLinkBuilder类来构造一个将用户带到特定目标的PendingIntent

我们将使用NavDeepLinkBuilder将应用程序小部件连接到目标。

  1. 打开DeepLinkAppWidgetProvider.kt
  2. 添加一个使用NavDeepLinkBuilder构造的PendingIntent

DeepLinkAppWidgetProvider

val args = Bundle()
args.putString("myarg", "From Widget");
val pendingIntent = NavDeepLinkBuilder(context)
        .setGraph(R.navigation.mobile_navigation)
        .setDestination(R.id.deeplink_dest)
        .setArguments(args)
        .createPendingIntent()

remoteViews.setOnClickPendingIntent(R.id.deep_link_button, pendingIntent)

注意:

  • setGraph包含导航图。
  • setDestination指定链接的目标位置。
  • setArguments包含您想要传递到深度链接的任何参数。
  1. 将深度链接小部件添加到您的主屏幕。长按主屏幕以查看添加小部件的选项。

长按

向下滚动查找小部件

完成后,您将拥有一个深度链接小部件。

395d37cfa7f5421f.png

  1. 点击小部件,并验证 Android 目标是否以正确的参数打开。由于您在 DeepLinkAppWidgetProvider 中传递的参数是“From Widget”,因此顶部应该显示“From Widget”。

17266d930c15160f.png

  1. 验证点击后退按钮是否会将您带到home_dest目标。

深度链接的返回堆栈是使用您传入的导航图确定的。如果您选择的显式 Activity 有一个父 Activity,则这些父 Activity 也包含在内。

返回堆栈是使用app:startDestination指定的目的地生成的。在这个应用程序中,我们只有一个 Activity 和一个导航级别,因此返回堆栈将带您到home_dest目标。

更复杂的导航可以包含嵌套导航图。嵌套图的每个级别的app:startDestination确定返回堆栈。有关深度链接和嵌套图的更多信息,请查看导航原则

11. 将网络链接与目标关联

深度链接最常见的用途之一是允许网络链接打开应用程序中的 Activity。传统上,您将使用intent-filter并将 URL 与您要打开的 Activity 关联

导航库使这变得非常简单,并允许您将 URL 直接映射到导航图中的目标。

<deepLink>是您可以添加到图中目标的元素。每个<deepLink>元素都有一个必需属性:app:uri

除了直接 URI 匹配之外,还支持以下功能:

  • 没有方案的 URI 假定为 http 和 https。例如,www.example.com将匹配http://www.example.comhttps://www.example.com
  • 您可以使用{placeholder_name}形式的占位符来匹配一个或多个字符。占位符的字符串值在参数 Bundle 中可用,该参数具有相同的键名。例如,http://www.example.com/users/{id}将匹配http://www.example.com/users/4
  • 可以使用通配符.*匹配零个或多个字符。
  • NavController 将自动处理ACTION_VIEW 意图并查找匹配的深层链接。

在此步骤中,您将向www.example.com添加一个深层链接。

  1. 打开mobile_navigation.xml
  2. deeplink_dest 目的地添加<deepLink>元素。

mobile_navigation.xml

<fragment
    android:id="@+id/deeplink_dest"
    android:name="com.example.android.codelabs.navigation.DeepLinkFragment"
    android:label="@string/deeplink"
    tools:layout="@layout/deeplink_fragment">

    <argument
        android:name="myarg"
        android:defaultValue="Android!"/>

    <deepLink app:uri="www.example.com/{myarg}" />
</fragment>
  1. 打开AndroidManifest.xml
  2. 添加nav-graph标签。这将确保生成适当的意图过滤器。

AndroidManifest.xml

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

    <nav-graph android:value="@navigation/mobile_navigation" />
</activity>
  1. 使用深层链接启动您的应用。有两种方法可以做到这一点
  • 使用adb
adb shell am start -a android.intent.action.VIEW -d "http://www.example.com/urlTest" 
  • 通过 Google 应用导航**。**您应该能够将 www.example.com/urlTest 放入搜索栏中,并且会显示消除歧义窗口。选择**导航 codelab**

使用搜索栏打开(不在 Chrome 中)

消除歧义对话框

无论哪种方式,您都应该在屏幕上看到消息“urlTest”。这是从 URL 传递到片段的。

4d22e0ddbda4d8cf.png

12. [可选] 尝试自行导航

codelab 应用还有一个部分供您尝试,那就是购物车按钮。

dabd03fdaaee4d91.png

这是您在此 codelab 中学习的技能的回顾。此步骤不包含注释,因此请自行尝试。

  1. 创建一个新的片段类
  2. 将片段作为目的地添加到导航图中
  3. 让购物车图标打开您的新片段类,使用 NavigationUI 处理菜单。

13. 恭喜!

您已经熟悉导航组件背后的基本概念!在此 codelab 中,您学习了:

  • 导航图结构
  • NavHostFragment 和 NavController
  • 如何导航到特定目的地
  • 如何通过操作导航
  • 如何在目的地之间传递参数,包括使用新的 safeargs 插件
  • 使用菜单、底部导航和导航抽屉进行导航
  • 通过深层链接导航

您可以继续使用此应用进行探索,或者开始在您自己的应用中使用导航。

还有更多内容可以尝试,包括:

  • 从返回堆栈中弹出目的地(或任何返回堆栈操作)
  • 嵌套导航图
  • 条件导航
  • 添加对新目的地的支持

有关导航组件的更多信息,请查看文档Jetpack Compose 导航 Codelab。如果您有兴趣了解其他架构组件,请尝试以下 codelab: