Jetpack 导航

导航架构组件可简化实现导航的过程,同时有助于您直观呈现应用的导航流程。该库具有诸多优势,包括:

  • 自动处理 fragment 事务
  • 默认情况下即可正确处理 upback
  • 提供默认的动画和过渡行为
  • 将深层链接视为第一类操作
  • 只需极少额外操作即可实现各种导航界面模式(例如抽屉式导航栏和底部导航栏)
  • 导航过程中,在传递信息的同时确保类型安全
  • 提供 Android Studio 工具,用于直观呈现和修改应用的导航流程

实践内容

在此 Codelab 中,您将处理一个如下所示的示例应用:

所有 activity 和 fragment 都已为您创建好了。您将使用导航组件来连接它们,并且在此过程中实现以下功能:

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

前提条件

  • Kotlin 基础知识(本 Codelab 采用 Kotlin)
  • Android Studio 3.2 或更高版本
  • 运行 API 14 或更高级别的模拟器或设备

获取代码

从 GitHub 克隆导航 Codelab:

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

或者,您可以下载代码库 Zip 文件:

下载 Zip 文件

获取 Android Studio 3.3 或更高版本

请确保您使用的是 Android Studio 3.3 或更高版本。这是使用 Android Studio 导航工具的必备条件。

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

导航概览

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

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

导航时,您将使用 NavController 对象,在导航图中向该对象指示您要去的地方或要使用的路径。NavController 随后会在 NavHostFragment 中显示相应的目的地。

这是基本思路。让我们从新的导航图资源开始,看看实际运用方式。

目的地

导航组件引入了目的地的概念。目的地是指您可在应用中导航到的任何位置,通常是 fragment 或 activity。开箱即可支持这两种类型的目的地,但您也可以根据需要创建自定义目的地类型

导航图是一种新的资源类型,它定义了用户可在应用中采取的所有可能路径。它直观地显示了从给定目的地可到达的所有目的地。Android Studio 会在其导航编辑器中显示导航图。以下是为应用创建的起始导航图的一部分:

Home、Step One 和 Step Two

探索导航编辑器

1. 打开 res/navigation/mobile_navigation.xml

2. 点击 Design 以进入设计模式:

您应看到以下内容:

该导航图显示了可用的目的地。目的地之间的箭头称为操作。您稍后将详细了解操作。

3. 点击某个目的地以查看其属性。

4. 点击任何以箭头表示的操作,以查看其属性。

导航 XML 文件详解

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

点击 Text 标签页:

您会看到类似如下所示的 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 目的地:

<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 定义 fragment 的 ID,您可以使用该 ID 在此 XML 和代码的其他地方引用该目的地。
  • android:name 在您导航到相应目的地时声明要实例化的 fragment 的完全限定类名。
  • tools:layout 指定图形编辑器中应显示的布局。

某些 <fragment> 标记还包含 <action><argument>,<deepLink>,我们稍后将逐一介绍这些标记。

示例应用从图中的几个目的地开始。在此步骤中,您将添加一个全新的目的地。您必须先向导航图添加目的地,然后才能导航到该目的地。

1. 打开 res/navigation/mobile_navigation.xml,然后点击 Design 标签页。

2. 点击 New Destination 图标,然后选择“settings_fragment”

系统会创建一个新目的地,并在设计视图中呈现该 fragment 布局的预览。

请注意,您还可以直接修改 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" />

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

Activity 和导航

导航组件遵循导航原则中介绍的指南。导航原则建议您将 activity 用作应用的入口点。activity 还将包括全局导航,例如底部导航栏。

相比之下,fragment 将是实际的目的地专用布局。

为此,您需要修改 activity 布局以包含一个名为 NavHostFragment 的特殊微件。当您使用导航图导航时,NavHostFragment 会换入和换掉不同的 fragment 目的地。

支持导航的简单布局如上图所示。您可以在 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>

请注意:

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

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

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

请注意,您需要传入目的地或操作 ID 才能进行导航。这些是导航图 XML 中定义的 ID。这是一个传递目的地 ID 的示例。

NavController 的功能非常强大,因为当您调用 navigate()popBackStack(), 等方法时,它会根据您正在导航的目标目的地类型或起始目的地类型来将这些命令转换为相应的框架操作。例如,当您使用 activity 目的地调用 navigate() 时,NavController 会为您调用 startActivity()

您可以通过多种方法获取与 NavHostFragment 关联的 NavController 对象。在 Kotlin 中,建议您根据是从 fragment、activity 还是视图中调用导航命令,使用以下某个扩展函数:

现在使用 NavController 进行导航。您将附加 Navigate To Destination 按钮,以导航到 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)
}

3. 运行应用,然后点击 Navigate To Destination 按钮。请注意,该按钮会导航到 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)
)

每个 navigate() 调用都具有相关联的不太吸引人的默认过渡,如下所示:

用户从主屏幕点击导航到目的地,然后导航到第 1 步。

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

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

添加自定义过渡

更新代码,以便按 Navigate To Destination 按钮会显示自定义过渡动画。

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)
}

3. 移除第 5 步中添加的代码(如果该代码仍存在)

4. 验证点按 Navigate To Destination 按钮是否会使 fragment 滑动到屏幕上,以及按返回按钮是否会使其滑出屏幕

操作

导航系统也支持通过操作进行导航。如前所述,导航图中显示的线条是操作的直观表现形式。

与按目的地导航相比,按操作导航具有以下优势:

  • 可以直观呈现在应用中的导航路径
  • 操作可以包含可设置的其他关联属性,例如过渡动画、参数值和返回堆栈行为
  • 可以使用 safe args 插件进行导航(稍后就会介绍)

以下是连接 flow_step_one_destflow_step_two_dest 的操作的截图和 XML:

<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_dest 连接到 home_dest 的操作的另一个示例:

<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_dest 连接到 home_dest 的操作使用相同的 ID next_action。您可以使用该 next_action ID 从 flow_step_one_destflow_step_two_dest 进行导航。此示例说明了操作如何提供抽象级别,以及如何根据上下文将您导航到其他位置。
  • 使用了 popUpTo 属性 - 此操作会从返回堆栈中弹出 fragment,直到您到达 home_dest

现在开始附加 Navigate with Action 按钮,使它具备应有的功能!

1. 在 Design 模式下打开 mobile_navigation.xml 文件

2. 将箭头从 home_dest 拖动到 flow_step_one_dest

3. 选择操作箭头(箭头变为蓝色)后,需更改操作的属性,以使:

  • ID = next_action
  • Enter 的“Transitions”属性 = slide_in_right
  • Exit 的“Transitions”属性 = slide_out_left
  • Pop Enter 的“Transitions”属性 = slides_in_left
  • Pop Exit 的“Transitions”属性 = slides_out_right

4. 点击 Text 标签页

请注意 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" />

5. 打开 HomeFragment.kt

6. 向 navigate_action_button 添加点击监听器

HomeFragment.kt

 view.findViewById<Button>(R.id.navigate_action_button)?.setOnClickListener(
        Navigation.createNavigateOnClickListener(R.id.next_action, null)
)

7. 验证点按 Navigate To Action 按钮是否会导航到下一个屏幕。

Safe Args

导航组件具有一个名为 safe args 的 Gradle 插件,该插件可生成简单的 object 和 builder 类,以便对为目的地和操作指定的参数进行类型安全的访问。

通过 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"
    //...
    }

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

app/build.gradle

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

android {
   //...
}

3. 打开 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 的类。

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

4. 打开 FlowStepFragment.kt

5. 注释掉以下代码行:

FlowStepFragment.kt

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

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

6. 更新 FlowStepFragment 以使用代码生成的类 FlowStepFragmentArgs。这将获得类型安全的 FlowStepFragment 参数:

FlowStepFragment.kt

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

Safe Args Direction 类

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

系统将为包含操作的每个不同目的地生成 Direction 类。Direction 类包含目的地具有的每项操作对应的方法。

例如,可以将 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)
}

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

使用带有选项菜单的 NavigationUI

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

1. 打开 MainActivity.kt

注意您已为 onCreateOptionsMenu 中的菜单 overflow_menu 扩充了代码

2. 打开 res/menu/overflow_menu.xml

3. 更新溢出菜单以包含 settings_dest

overflow_menu.xml

<item
    android:id="@+id/settings_dest"
    android:icon="@drawable/ic_settings"
    android:menuCategory="secondary"
    android:title="@string/settings" />

4. 打开 MainActivity.kt

5. 让 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)
}

6. 运行应用。您应具有一个能够导航到 SettingsFragment 的 ActionBar 功能菜单。

使用 NavigationUI 配置底部导航栏

该代码已包含用于实现底部导航栏的 XML 布局代码,这就是您看到底部导航栏的原因。但该导航栏目前不支持导航操作。

1. 打开 res/layout/navigation_activity/navigation_activity.xml (h470dp) 并点击 Text 标签页

请注意存在用于底部导航栏的 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" />

2. 打开 res/menu/bottom_nav_menu.xml

请注意有两个用于底部导航栏的项目,它们的 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 来使底部导航栏能够支持一些导航操作。

3. 打开 MainActivity.kt

4. 使用 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 导航。

2. 打开 MainActivity.kt

3. 使用 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 的用途是指定工具栏、折叠工具栏和操作栏的配置选项。配置选项包括工具栏是否必须处理抽屉式导航栏布局,以及哪些目的地被视为顶级目的地

顶级目的地是应用的根级目的地。这些目的地不会在应用栏中显示“向上”按钮,如果目的地使用抽屉式导航栏布局,那么这些目的地会显示抽屉式导航栏图标。

4. 通过传递一组顶级目的地 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 中显示标题
  • 当您位于顶级目的地时,显示 Up 按钮
  • 当您位于顶级目的地时,显示抽屉式导航栏图标(汉堡图标)

5. 实现 setupActionBarWithNavController

MainActivity.kt

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

您还应让 NavigationUI 处理按 Up 按钮后的操作。

6. 使用相同的 AppBarConfiguration 替换 onSupportNavigationUp 并调用 NavigationUI.navigateUp

MainActivity.kt

override fun onSupportNavigateUp(): Boolean {
    return findNavController(R.id.my_nav_host_fragment).navigateUp(appBarConfiguration)
}

7. 运行您的代码。如果您以分屏模式打开应用,那么抽屉式导航栏应该会正常运行。向上图标和抽屉式导航栏图标应在适当的时间显示并且可以正常运行。

NavigationView 添加新目的地非常简单。在抽屉式导航栏支持向上和返回导航后,您只需添加新菜单项即可。

8. 打开 menu/nav_drawer_menu.xml

9. 为 settings_dest 添加新菜单项

<item
    android:id="@+id/settings_dest"
    android:icon="@drawable/ic_settings"
    android:title="@string/settings" />

现在,抽屉式导航栏会将“Settings”屏幕显示为目的地。做得好!

导航组件还支持深层链接。深层链接是一种跳转到应用导航中心的方式,无论是来自实际网址链接,还是来自通知的待处理 intent。

使用导航库处理深层链接的一个好处是:它可以确保用户从其他入口点(如应用微件、通知或网页链接)通过适当的返回堆栈从正确的目的地开始(将在下一步中进行介绍)。

导航提供了一个 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 包含您想要传递给深层链接的所有参数。

3. 将深层链接微件添加到主屏幕。点按并按住主屏幕,以查看添加微件的选项。

点按并按住

向下滚动以找到微件

完成后,您会看到一个深层链接微件。

4. 点按微件,验证 Android 目的地是否使用正确的参数打开。它应该在顶部显示“From Widget”,因为这是您在 DeepLinkAppWidgetProvider 中传递的参数。

5. 验证按返回按钮后是否会转到 home_dest 目的地。

深层链接的返回堆栈通过您传入的导航图确定。如果您选择的显式 activity 具有父 activity,那么这些父 activity 也会相应包括在内。

返回堆栈是使用 app:startDestination 指定的目的地生成的。在此应用中,我们只有一个 activity 和一个导航层级,因此返回堆栈会将您引导至 home_dest 目的地。

更复杂的导航可能会包含嵌套导航图。嵌套图的每个级别的 app:startDestination 决定了返回堆栈。如需详细了解深层链接和嵌套图,请参阅导航原则

深层链接最常见的用途之一就是允许网页链接在您的应用中打开 activity。按照历来的做法,您需要使用 intent 过滤器并将网址与您要打开的 activity 相关联

导航库简化了此操作,支持将网址直接映射到导航图中的目的地。

<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
  • 您可以使用 .* 通配符匹配 0 个或多个字符。
  • NavController 会自动处理 ACTION_VIEW intent,并查找匹配的深层链接。

在这一步中,您将添加指向 www.example.com 的深层链接。

1. 打开 mobile_navigation.xml

2. 将 <deepLink> 元素添加到 deeplink_dest 目的地。

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>

3. 打开 AndroidManifest.xml

4. 添加 nav-graph 标记。这将确保生成适当的 intent 过滤器

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>

5. 使用深层链接启动您的应用。您可以采用下列两种方法:

  • 使用 adb
adb shell am start -a android.intent.action.VIEW -d "http://www.example.com/urlTest" 
  • 通过 Google 应用进行导航。您应该能够将 www.example.com/urlTest 放置在搜索栏中,系统会显示消除歧义窗口。选择 Navigation codelab

使用搜索栏打开

(不适用于 Chrome)

消除歧义对话框

无论使用哪种方式,屏幕上应该都会显示“urlTest”消息。该消息会从网址传递到 fragment。

对于此 Codelab 应用,您还可以试着操作购物车按钮。

下面回顾一下您在本 Codelab 中学到的技能。此步骤不提供注释,您可以自行尝试进行操作:

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

您已熟悉有关导航组件的基本概念!在本 Codelab 中,您学到了以下知识:

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


您可以继续通过此应用进行探索,也可以开始在您自己的应用中使用导航功能。

还有很多可以尝试的操作,包括:

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

如需详细了解导航组件,请参阅相关文档。如需了解其他架构组件,可以尝试学习以下 Codelab: