Navigation 组件使用入门

本主题介绍了如何设置和使用 Navigation 组件。如需简要了解 Navigation 组件,请参阅导航概览

设置您的环境

如需在您的项目中添加 Navigation 支持,请向应用的 build.gradle 文件添加以下依赖项:

Groovy

dependencies {
  def nav_version = "2.5.3"

  // Java language implementation
  implementation "androidx.navigation:navigation-fragment:$nav_version"
  implementation "androidx.navigation:navigation-ui:$nav_version"

  // Kotlin
  implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
  implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

  // Feature module Support
  implementation "androidx.navigation:navigation-dynamic-features-fragment:$nav_version"

  // Testing Navigation
  androidTestImplementation "androidx.navigation:navigation-testing:$nav_version"

  // Jetpack Compose Integration
  implementation "androidx.navigation:navigation-compose:$nav_version"
}

Kotlin

dependencies {
  val nav_version = "2.5.3"

  // Java language implementation
  implementation("androidx.navigation:navigation-fragment:$nav_version")
  implementation("androidx.navigation:navigation-ui:$nav_version")

  // Kotlin
  implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
  implementation("androidx.navigation:navigation-ui-ktx:$nav_version")

  // Feature module Support
  implementation("androidx.navigation:navigation-dynamic-features-fragment:$nav_version")

  // Testing Navigation
  androidTestImplementation("androidx.navigation:navigation-testing:$nav_version")

  // Jetpack Compose Integration
  implementation("androidx.navigation:navigation-compose:$nav_version")
}

如需了解如何向您的项目添加其他架构组件,请参阅向项目添加组件

创建导航图

导航发生在应用中的各个目的地(即您的应用中用户可以导航到的任意位置)之间。这些目的地是通过操作连接的。

导航图是一种资源文件,其中包含您的所有目的地和操作。该图表会显示应用的所有导航路径。

图 1 直观显示了一个示例应用的导航图,该应用包含 6 个目的地(通过 5 个操作连接)。每个目的地均由一个预览缩略图表示,连接操作由箭头表示,该箭头表示用户可以如何从一个目的地导航到另一个目的地。

图 1. 一个导航图,显示了由 5 个操作连接的 6 个不同目的地的预览。
  1. “目的地”是指应用中的不同内容区域。
  2. “操作”是指目的地之间的逻辑连接,表示用户可以采取的路径。

如需向项目添加导航图,请执行以下操作:

  1. 在“Project”窗口中,右键点击 res 目录,然后依次选择 New > Android Resource File。此时系统会显示 New Resource File 对话框。
  2. File name 字段中输入名称,例如“nav_graph”。
  3. Resource type 下拉列表中选择 Navigation,然后点击 OK

当您添加首个导航图时,Android Studio 会在 res 目录内创建一个 navigation 资源目录。该目录包含您的导航图资源文件(例如 nav_graph.xml)。

添加图表后,Android Studio 会在 Navigation Editor 中打开该图表。在 Navigation Editor 中,您可以直观地修改导航图,或直接修改底层 XML。

图 2. Navigation Editor
  1. Destinations panel:列出了导航宿主和目前位于 Graph Editor 中的所有目的地。
  2. Graph Editor:包含导航图的视觉表示形式。您可以在 Design 视图和 Text 视图中的底层 XML 表示形式之间切换。
  3. Attributes:显示导航图中当前所选项的属性。

点击 Text 标签页可查看相应的 XML,它应类似于以下代码段:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:id="@+id/nav_graph">

</navigation>

<navigation> 元素是导航图的根元素。当您向图表添加目的地和连接操作时,可以看到相应的 <destination><action> 元素在此处显示为子元素。如果您有嵌套图表,它们将显示为子 <navigation> 元素。

向 Activity 添加 NavHost

导航宿主是 Navigation 组件的核心部分之一。导航宿主是一个空容器,用户在您的应用中导航时,目的地会在该容器中交换进出。

导航宿主必须派生于 NavHost。Navigation 组件的默认 NavHost 实现 (NavHostFragment) 负责处理 fragment 目的地的交换。

通过 XML 添加 NavHostFragment

以下 XML 示例显示了作为应用主 Activity 一部分的 NavHostFragment

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.appcompat.widget.Toolbar
        .../>

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"

        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        .../>

</androidx.constraintlayout.widget.ConstraintLayout>

请注意以下几点:

  • android:name 属性包含 NavHost 实现的类名称。
  • app:navGraph 属性将 NavHostFragment 与导航图相关联。导航图会在此 NavHostFragment 中指定用户可以导航到的所有目的地。
  • app:defaultNavHost="true" 属性确保您的 NavHostFragment 会拦截系统返回按钮。请注意,只能有一个默认 NavHost。如果同一布局(例如,双窗格布局)中有多个宿主,请务必仅指定一个默认 NavHost

您也可以使用布局编辑器向 activity 添加 NavHostFragment,具体操作步骤如下:

  1. 在项目文件列表中,双击 Activity 的布局 XML 文件,以在 Layout Editor 中将其打开。
  2. Palette 窗格内,选择 Containers 类别,或者搜索“NavHostFragment”。
  3. NavHostFragment 视图拖动到您的 Activity 上。
  4. 接下来,在随即显示的 Navigation Graphs 对话框中,选择需要与此 NavHostFragment 相关联的相应导航图,然后点击 OK

向导航图添加目的地

您可以从现有的 Fragment 或 Activity 创建目的地。您还可以使用 Navigation Editor 创建新目的地,或创建占位符以便稍后替换为 fragment 或 activity。

在本示例中,我们来创建一个新目的地。如需使用 Navigation Editor 添加新目的地,请执行以下操作:

  1. 在 Navigation Editor 中,点击 New Destination 图标 ,然后点击 Create new destination
  2. 在随即显示的 New Android Component 对话框中,创建您的 Fragment。如需详细了解 Fragment,请参阅 Fragment 文档

当您返回到 Navigation Editor 中时,会发现 Android Studio 已将此目的地添加到图中。

图 1 显示了目的地和占位符目的地的示例。

图 3. 目的地和占位符

如需了解向导航图添加目的地的其他方式,请参阅创建目的地

目的地详解

点击一个目的地以将其选中,并注意 Attributes 面板中显示的以下属性:

  • Type 字段指示在您的源代码中,该目的地是作为 fragment、activity 还是其他自定义类实现的。
  • Label 字段包含该目的地的用户可读名称。例如,如果您使用 setupWithNavController()NavGraph 连接到 Toolbar,就可能在界面上看到此字段。因此,我们建议您对此值使用资源字符串。
  • ID 字段包含该目的地的 ID,它用于在代码中引用该目的地。
  • Class 下拉列表显示与该目的地相关联的类的名称。您可以点击此下拉列表,将相关联的类更改为其他目的地类型。

点击 Text 标签页可查看导航图的 XML 视图。XML 中同样包含该目的地的 idnamelabellayout 属性,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    app:startDestination="@id/blankFragment">
    <fragment
        android:id="@+id/blankFragment"
        android:name="com.example.cashdog.cashdog.BlankFragment"
        android:label="@string/label_blank"
        tools:layout="@layout/fragment_blank" />
</navigation>

将某个屏幕指定为起始目的地

起始目的地是用户打开您的应用时看到的第一个屏幕,也是用户退出您的应用时看到的最后一个屏幕。Navigation Editor 使用房子图标 表示起始目的地。

所有目的地就绪后,您便可以选择起始目的地,具体操作步骤如下:

  1. Design 标签页中,点击相应目的地,使其突出显示。

  2. 点击 Assign start destination 按钮 。或者,您可以右键点击该目的地,然后点击 Set as Start Destination

连接目的地

操作是指目的地之间的逻辑连接。操作在导航图中以箭头表示。操作通常会将一个目的地连接到另一个目的地,不过您也可以创建全局操作,此类操作可让您从应用中的任意位置转到特定目的地。

借助操作,您可以表示用户在您的应用中导航时可以采取的不同路径。请注意,如需实际导航到各个目的地,您仍然需要编写代码以执行导航操作。如需了解详情,请参阅本主题后面的导航到目的地部分。

您可以使用 Navigation Editor 将两个目的地连接起来,具体操作步骤如下:

  1. Design 标签页中,将鼠标悬停在目的地的右侧,该目的地为您希望用户从中导航出来的目的地。该目的地右侧上方会显示一个圆圈,如图 4 所示。

    图 4. 一个包含操作连接圆圈的目的地
  2. 点击您希望用户导航到的目的地,并将光标拖动到该目的地的上方,然后松开。这两个目的地之间生成的线条表示操作,如图 5 所示。

    图 5. 通过操作连接目的地
  3. 点击箭头以突出显示该操作。此时 Attributes 面板中会显示以下属性:

    • Type 字段包含“Action”。
    • ID 字段包含该操作的 ID。
    • Destination 字段包含目的地 Fragment 或 Activity 的 ID。
  4. 点击 Text 标签,以切换到 XML 视图。现在,一个 action 元素已添加到源目的地中。该操作有一个 ID 和一个目的地属性(其中包含下一个目的地的 ID),如以下示例所示:

    <?xml version="1.0" encoding="utf-8"?>
    <navigation xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:android="http://schemas.android.com/apk/res/android"
        app:startDestination="@id/blankFragment">
        <fragment
            android:id="@+id/blankFragment"
            android:name="com.example.cashdog.cashdog.BlankFragment"
            android:label="@string/label_blank"
            tools:layout="@layout/fragment_blank" >
            <action
                android:id="@+id/action_blankFragment_to_blankFragment2"
                app:destination="@id/blankFragment2" />
        </fragment>
        <fragment
            android:id="@+id/blankFragment2"
            android:name="com.example.cashdog.cashdog.BlankFragment2"
            android:label="@string/label_blank_2"
            tools:layout="@layout/fragment_blank_fragment2" />
    </navigation>
    

在导航图中,操作由 <action> 元素表示。操作至少应包含自己的 ID 和用户应转到的目的地的 ID。

导航到目的地是使用 NavController 完成的,它是一个在 NavHost 中管理应用导航的对象。每个 NavHost 均有自己的相应 NavController。您可以使用以下方法之一检索 NavController

Kotlin

Java

使用 FragmentContainerView 创建 NavHostFragment,或通过 FragmentTransaction 手动将 NavHostFragment 添加到您的 Activity 时,尝试通过 Navigation.findNavController(Activity, @IdRes int) 检索 Activity 的 onCreate() 中的 NavController 将失败。您应改为直接从 NavHostFragment 检索 NavController

Kotlin

val navHostFragment =
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController

Java

NavHostFragment navHostFragment =
        (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
NavController navController = navHostFragment.getNavController();

使用 Safe Args 确保类型安全

如需在目的地之间导航,建议使用 Safe Args Gradle 插件。该插件可以生成简单的对象和构建器类,这些类支持在目的地之间进行类型安全的导航和参数传递。

如需将 Safe Args 添加到您的项目,请在顶层 build.gradle 文件中包含以下 classpath

Groovy

buildscript {
    repositories {
        google()
    }
    dependencies {
        def nav_version = "2.5.3"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }
}

Kotlin

buildscript {
    repositories {
        google()
    }
    dependencies {
        val nav_version = "2.5.3"
        classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version")
    }
}

您还必须应用以下两个可用插件之一。

如需生成适用于 Java 模块或 Java 和 Kotlin 混合模块的 Java 语言代码,请将以下行添加到应用或模块build.gradle 文件中:

Groovy

plugins {
  id 'androidx.navigation.safeargs'
}

Kotlin

plugins {
    id("androidx.navigation.safeargs")
}

此外,如需生成仅适用于 Kotlin 模块的 Kotlin 语言代码,请添加以下行:

Groovy

plugins {
  id 'androidx.navigation.safeargs.kotlin'
}

Kotlin

plugins {
    id("androidx.navigation.safeargs.kotlin")
}

根据迁移到 AndroidX 文档,您的 gradle.properties 文件中必须具有 android.useAndroidX=true

启用 Safe Args 后,该插件会生成代码,其中包含您定义的每个操作的类和方法。对于每个操作,Safe Args 还会为每个源目的地(生成相应操作的目的地)生成一个类。生成的类的名称由源目的地类的名称和“Directions”一词组成。例如,如果目的地的名称为 SpecifyAmountFragment,生成的类的名称为 SpecifyAmountFragmentDirections。生成的类为源目的地中定义的每个操作提供了一个静态方法。该方法会将任何定义的操作参数作为参数,并返回可传递到 navigate()NavDirections 对象。

例如,假设我们的导航图包含一个操作,该操作将源目的地 SpecifyAmountFragment 和接收目的地 ConfirmationFragment 连接起来。

Safe Args 会生成一个 SpecifyAmountFragmentDirections 类,其中只包含一个 actionSpecifyAmountFragmentToConfirmationFragment() 方法(该方法会返回 NavDirections 对象)。然后,您可以将返回的 NavDirections 对象直接传递到 navigate(),如以下示例所示:

Kotlin

override fun onClick(view: View) {
    val action =
        SpecifyAmountFragmentDirections
            .actionSpecifyAmountFragmentToConfirmationFragment()
    view.findNavController().navigate(action)
}

Java

@Override
public void onClick(View view) {
    NavDirections action =
        SpecifyAmountFragmentDirections
            .actionSpecifyAmountFragmentToConfirmationFragment();
    Navigation.findNavController(view).navigate(action);
}

如需详细了解如何使用 Safe Args 在目的地之间传递数据,请参阅使用 Safe Args 传递类型安全的数据

更多信息

如果您遇到任何与 Navigation 相关的问题,请通过以下渠道之一提交反馈:

如需了解如何在错误报告中提供最有用的信息,请访问以下链接: