一、前言
传统的应用开发,一般都是采用一个界面一个 Activity 的形式,但是大家都知道, Activity 在 Android 中是属于重量级的组件,从而导致程序资源消耗大,用户体验不佳。而导航组件 Navigation 采用的是 Fragment 轻量级的组件实现的,可以节省资源,提高用户体验。
二、导航简介
导航组件是 Android Jetpack 的一部分,主要用途是实现用户导航、进入和退出应用中不同内容片段的交互,不论是普通的按钮点击,还是应用栏、抽屉导航栏等复杂的模式,它都能轻松应对,当然,导航组件也有它既定的 导航原则 来确保一致且可预测的用户体验。
2.1 导航组件的组成
导航组件主要有三部分组成:
- 导航图(
navGraph):这是包含所有导航相关信息的XML资源,这些信息包括应用内所有内容区域个体(称为目标,一般都是Fragment),以及用户可以通过应用跳转的可能路径。 - 导航宿主(
NavHost):这是用来显示导航图中声明的目标的一个空白容器。导航组件包含一个默认的NavHost实现 (NavHostFragment),可用于显示Fragment目标。 - 导航控制器(
NavController):在NavHost中管理应用导航的目标,当用户在应用中进行操作时,导航控制器会控制目标的切换。
使用导航组件有各种优势,包括以下方面:
- 自动处理
Fragment事务; - 在默认情况下,能够正确处理往返操作;
- 支持动画和转场动画;
- 支持导航界面模式(例如:抽屉式导航栏和底部导航栏)
- Safe Args 支持(一种可在导航和目标之间传递数据时提供类型安全的 Gradle 插件)
ViewModel支持- 可以使用Android Studio的 Navigation Editor 来编辑和查看导航图(必须使用AnroidStudio 3.3及以上版本)
2.2 导航的原则
在使用导航组件时,应当遵循一些原则,以提高用户体验。
注意:即使您未在项目中使用 Navigation 组件,您的应用也应遵循这些设计原则。
2.2.1 固定的起始目的地
顾名思义,您构建的应用必须有一个固定的起始目的地,这个起始目的地就是指当应用启动时蛋刀的第一个屏幕。起始目的地也是用户按返回按钮后,在回到启动器前看到的最后一个屏幕。
示例:

以上示例中,用户登录页面就是起始目的地,点击启动器图标打开应用,第一个启动的页面就是用户登录页面,在返回过程中,最后一个呈现的页面也是登录页面。
2.2.2 导航状态表现为目的地堆栈
在用户启动应用时,系统会启动一个新任务,并且显示起始目的地,这个起始目的地是应用导航的而基础。当用户在应用中进行导航时,栈顶的目标就是显示在屏幕上的,而栈内的所有目标都是历史记录。
导航组件会为你管理所有返回栈的顺序,当然你也可以自行管理,已达到某些目的。
2.2.3 在应用的任务中向上按钮和返回按钮行为相同
首先,说一下什么是向上按钮,向上按钮是指在应用中的返回上一级的按钮(一般是在用户导航栏中),返回按钮则是系统导航中的返回按钮。在应用的任务重,向上按钮和返回按钮的行为相同,都是将栈顶的目标移除,返回到上一个目标。
2.2.4 向上按钮不会退出应用
在应用的任务重,向上按钮可以返回到上一个目标,但是绝不会退出应用。
2.2.5 深度链接可以模拟手动导航
无论是通过深度链接至特定的目的地,还是手动导航到特定目的地,都可以使用向上按钮通过各个目的地导航回到起始目的地。当深度链接至特定的目的地时,会移除所有返回栈中的任务,并替换为深度链接的返回栈。值得注意的是,深度链接合成的返回栈是一个完整的返回栈,他跟手动导航至特定目的地具有相同的返回栈,这个是非常重要的,因为合成的返回栈必须是真实的。
三、使用入门
本文对应的Demo源码请访问Github:NavigationDemo
3.1 添加依赖
在应用模块目录下的 build.gradle文件中添加 dependencies 依赖声明。
dependencies {
def nav_version = "2.2.2"
// 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"
// Dynamic Feature Module Support
implementation "androidx.navigation:navigation-dynamic-features-fragment:$nav_version"
// Testing Navigation
androidTestImplementation "androidx.navigation:navigation-testing:$nav_version"
}
3.2 创建导航图资源文件
导航是发生在各个目的地之间的,而这些目的地通过操作连接在一起。导航图是一种资源文件,它包含了所有的目的地和操作的声明。
创建导航图资源文件,可以按以下不走进行:
- 在项目程序模块下面的
res目录下,右键-》“New”-》“Android Resource File”; - 在弹出的窗口中输入文件名;
- 在“Resource type”中选择“Navigation”;
- 点击确定创建资源文件。

新建的导航图资源文件是一个 XML 资源文件,以 navigation 为根节点,大致内容如下。
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph">
</navigation>
<navigation> 元素是导航图的根元素。当您向图表添加目的地和连接操作时,可以看到相应的 <destination> 和 <action> 子元素。如果有嵌套图表,也会出现 <navigation> 子元素。
如果是首次添加导航图资源,Android Studio 会在
res目录内创建一个navigation资源目录,该目录包含您的导航图资源文件。当然,如果你已经够熟练,可以直接创建目录和文件的方式创建。
3.2.1 Android Studio中的Navigation Editor
Android Studio提供了强大的导航编辑器,在这里不但可以预览您所添加的目标,还可以修改导航图,可以通过拖动的方式或者直接编码修改底层 XML 的方式修改导航图。为了方便项目的维护和代码可读性,笔者更加建议使用修改底层 XML 的方式,或者结合修改底层 XML 的方式。
温馨提示:不同版本的Android Studio的界面操作有些不一样,不少从旧版升级到3.6之后,发现打开资源文件的时候,默认是 “Design” 模式(包括
layout布局资源),一时间找不到北了,不知道如何切换成修改底层 XML 的模式。其实这是3.6 版本之后的小变动,在旧版本只有 “Code” 和 “Design” 两种模式,新版有 “Code”、“Split” 、“Design”三种模式,而且模式切换的位置也变了,旧版是在左下角,而新版是在右上角,而且是图标的形式(如下图)。
3.3 向 Activity 添加导航宿主(NavHost)
导航宿主是导航组件的核心部分之一,导航宿主是一个空容器,用来存放和处理目的地。导航宿主必须派生于 NavHost,NavHostFragment 是导航组件的默认导航宿主实现,负责处理 Fragment 目的地的交换。
注意:导航组件的设计理念是用于具有一个主
Activity和多个Fragment目的地的应用,主Activity与导航图相关联,并且包含一个负责根据需要交换目的地的NavHostFragment。如果您的应用需要在多个Activity上实现导航,就需要为每个Activity添加导航宿主,并在每个Activity关联其自己的导航图。
3.3.1 通过 XML 添加 NavHostFragment
在主 Activity 的布局文件中,添加 <fragment> 标签,并在内部指定导航图,如下:
<?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">
<fragment
android:id="@+id/fragment_main"
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" />
</androidx.constraintlayout.widget.ConstraintLayout>
详细解说:
android:name属性包含NavHost实现类的名称(示例中使用的是默认实现NavHostFragment,如果有需要,可以使用自定义的Fragment类,但是必须实现NavHost或者继承NavHostFragment)app:navGraph属性将导航宿主(NavHostFragment)与导航图关联,指向包含所有导航目的地的导航图资源文件app:defaultNavHost="true"属性确保导航宿主会拦截系统返回按钮。请注意,只能有一个默认导航宿主,如果同一布局(例如,双窗格布局)中有多个导航宿主,请务必仅指定一个默认导航宿主。
说明:导航组件是 Android Jetpack 部分,不属于 Android 系统组件,所以需要在布局中添加属性引入,如:
xmlns:app="http://schemas.android.com/apk/res-auto"
除此之外,还可以使用 Layout Editor 向 Activity 添加导航宿主,具体步骤如下:
- 打开 Activity 布局文件,切换到 “Design” 窗口;
- 在 “Palette” 窗口选择 “Containers” ,然后找到 “NavHostFragment”(可直接搜索);
- 将 “NavHostFragment” 拖向布局中;
- 在弹出的窗口中选择导航图,然后确定;
- 在 “Properties” 窗口设置相关属性。
3.3.2 向导航图中添加目的地
对于懒于编码的小伙伴可以使用 Navigation Editor 向导航中添加目的地,因为这些都是用户引导模式的,没什么可说,我这里主要讲一下手动添加目的地的步骤:
- 新建
Fragment类和布局文件,并实现相关逻辑代码; - 在导航图 XML 中新增
<fragment>标签; - 配置
<fragment>标签的相关属性,如:android:id、android:name、android:lable等;
示例:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/loginFragment">
<fragment
android:id="@+id/loginFragment"
android:name="com.owen.navigationdemo.LoginFragment"
android:label="LoginFragment"
tools:layout="@layout/fragment_login">
<action
android:id="@+id/action_loginFragment_to_registerFragment"
app:destination="@id/registerFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right"/>
</fragment>
<fragment
android:id="@+id/registerFragment"
android:name="com.owen.navigationdemo.RegisterFragment"
android:label="RegisterFragment"
tools:layout="@layout/fragment_register">
</fragment>
</navigation>
目的地属性详解
Type:即标签名称,指示在源代码中,该目的地是作为Fragment、Activity还是其他自定义类实现的anroid:label:这个属性指定目的地的名称android:id: 这个属性指定改目的地的ID,用于在代码中引用该目的地android:name:这个属性用来指定目的地所关联的类
除此之外,还可以通过tools:layout属性指定预览布局文件,这样就可以在导航图编辑器中看到对应的布局预览。
3.3.3 将某个目的地指定为起始目的地
导航的原则之一就是固定的起始目的地,指定起始目的地的方法有两种,一种是使用 Navigation Editor,在 “Design”窗口中,选中需要指定为起始目的地的目标,点击 “房子”图标(如下图)即可。另一种方法就是在 XML 源代码中,在 <navigation> 标签中添加 app:startDestination 属性进行指定,属性值为需要指定的目的地的ID(如下示例)。

示例:通过 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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/loginFragment">
<fragment
android:id="@+id/loginFragment"
android:name="com.owen.navigationdemo.LoginFragment"
android:label="LoginFragment"
tools:layout="@layout/fragment_login">
<action
android:id="@+id/action_loginFragment_to_registerFragment"
app:destination="@+id/registerFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right"/>

本文详细介绍Android导航组件的原理与实践,涵盖导航图、导航宿主、控制器及目的地配置,探讨不同类型的导航目的地、嵌套导航图、全局操作、数据传递、动画效果及深层链接创建。

最低0.47元/天 解锁文章
769





