Android Jetpack - Navigation 组件:进行应用程序导航
一. Navigation 组件的介绍
1.1 什么是 Navigation 组件
- Navigation 组件是一种 Android Jetpack 库,它可以帮助开发者轻松地实现应用程序中的导航功能。导航组件包含多个类和组件,包括导航图、目的地、导航控制器等,可以帮助我们管理应用程序中的页面导航和任务导航。通过使用 Navigation 组件,我们可以更加方便地实现应用程序的导航功能,同时也可以提高应用程序的用户体验。在本篇文章中,我们将介绍如何使用 Navigation 组件来实现应用程序导航,并提供一些示例和更多的扩展功能。
1.2 Navigation 组件的优势
- Navigation 组件可以轻松实现应用程序中的导航,包括页面之间的转换和应用程序内部的导航。
- Navigation 组件可以提高应用程序的可维护性和可扩展性,因为它们使得应用程序的结构更加清晰,并且可以更容易地添加新的功能和页面。
- Navigation 组件可以提供一致的用户体验,因为它们使用了标准的导航模式和动画效果。
- Navigation 组件可以帮助开发人员更快地构建应用程序,因为它们提供了许多常见的导航模式和功能,可以直接使用或进行修改。
- Navigation 组件可以提高应用程序的可测试性,因为它们使得页面之间的导航和状态转换更加明确和可控。
1.3 Navigation 组件主要由3个部分组成:
- NavHost:用来嵌入导航流程的容器,一般使用
FragmentContainerView
。 - NavController:负责在
NavHost
内部处理导航事务的控制器,用于执行页面跳转、管理返回栈等。 - NavGraph:描述
Fragment
之间导航关系的资源文件,在其中定义页面之间的转跳、动画等。一般放在res/navigation/
目录下。
简而言之,Navigation
组件通过在NavHost
中使用NavGraph
来描述Fragment
导航路径与关系,然后由NavController
来执行实际的导航工作,这样极大地简化了以往的页面跳转逻辑和回退栈管理流程。
二. Navigation 组件的基本使用
2.1 添加导航组件到项目中
- 在项目的 build.gradle 文件中添加以下依赖:
dependencies {def nav_version = "2.5.3"implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}
- 在布局文件中添加 NavHostFragment:
<androidx.fragment.app.FragmentContainerViewandroid:id="@+id/nav_host_fragment"android:name="androidx.navigation.fragment.NavHostFragment"android:layout_width="match_parent"android:layout_height="match_parent"app:navGraph="@navigation/nav_graph" />
- 正确获取 NavController 对象 :
在 Activity 内使用 NavController 时,应在onCreate()
中获取:
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
NavigationUI.setupActionBarWithNavController(this, navController)
而在 Fragment 内,应在onAttach()
或onViewCreate()
中获取:
val navHostFragment = parentFragment as NavHostFragment
val navController = navHostFragment.navController
NavHost
需要指定app:navGraph
属性来关联一个导航图NavGraph
,这决定了其中Fragment
页面之间的导航关系和跳转路径。
NavController
是 Navigation
组件的控制中心,用于在NavHost
内执行导航操作。可以在Activity
或Fragment
中通过NavHostFragment
的navController
属性获取对应的NavController
实例。
常见的导航操作有:
- 导航到目标目的地:
navController.navigate(R.id.destination_id)
- 回退一个目的地:
navController.navigateUp()
或navController.popBackStack()
- 回退到根目的地:
navController.popBackStack(R.id.root_destination, false)
NavController
还负责维护Fragment
的回退栈,以及在按返回按钮时正确出栈,这大大简化了之前管理Fragment
事务的复杂度。
通过NavHost
和NavController
的配合,Navigation
组件实现了在NavGraph
中声明的导航逻辑和页面切换功能。这使Fragment
之间的导航变得极为简单高效。开发者只需关注于定义NavGraph
,并调用NavController
中的导航方法即可实现页面跳转,其余的一切尽在Navigation
组件的掌控之中。
2.2 创建导航图
- 在XML文件中创建导航图:
<androidx.fragment.app.FragmentContainerViewandroid:id="@+id/nav_host_fragment"android:name="androidx.navigation.fragment.NavHostFragment"android:layout_width="match_parent"android:layout_height="match_parent"app:navGraph="@navigation/nav_graph" />
- 在res文件夹下创建一个
navigation
文件夹,然后在该文件夹下创建一个nav_graph.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"android:id="@+id/nav_graph"app:startDestination="@id/firstFragment"><fragmentandroid:id="@+id/firstFragment"android:name="com.smallmarker.jetpackpractice.navigation.fragment.FirstFragment"android:label="First"tools:layout="@layout/fragment_first"><actionandroid:id="@+id/action_firstFragment_to_secondFragment"app:destination="@+id/secondFragment" /></fragment><fragmentandroid:id="@+id/secondFragment"android:name="com.smallmarker.jetpackpractice.navigation.fragment.SecondFragment"android:label="Second"tools:layout="@layout/fragment_second" /></navigation>
在导航图中,<fragment>
元素用于定义目的地,android:id
属性用于指定目的地的唯一标识符,android:name
属性用于指定目的地的类名,android:label
属性用于指定目的地在应用程序中显示的标签名称。
<action>
元素用于定义动作,android:id
属性用于指定动作的唯一标识符,app:destination
属性用于指定动作要执行的目的地。
三. Navigation 组件的高级使用
3.1 深层链接
在 Android 中,深层链接是指将用户直接转到应用内特定目的地的链接。借助 Navigation 组件,您可以创建两种不同类型的深层链接:显式深层链接和隐式深层链接。
- 创建显式深层链接:
显式深层链接是深层链接的一个实例,该实例使用 PendingIntent
将用户转到应用内的特定位置。例如,您可以在通知或应用 widget
中显示显式深层链接。
val pendingIntent = NavDeepLinkBuilder(it).setGraph(R.navigation.nav_deep_link).setDestination(R.id.deepLinkFragment).setArguments(Bundle().apply {putInt("id", 1)}).setComponentName(DeepLinkActivity::class.java).createPendingIntent()val notification = NotificationCompat.Builder(it, "my_channel").setContentTitle("Title").setContentText("测试深层链接").setSmallIcon(R.mipmap.ic_launcher).setContentIntent(pendingIntent).build()NotificationManagerCompat.from(it).notify(Random.nextInt(10), notification)
该示例使用 NavDeepLinkBuilder
类构造 PendingIntent
, 添加到通知中并发送,点击通知跳转指定页面。
- 创建隐式深层链接:
<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_deep_link"app:startDestination="@id/deepLinkFragment"><fragmentandroid:id="@+id/deepLinkFragment"android:name="com.smallmarker.jetpackpractice.navigation.fragment.DeepLinkFragment"android:label="DeepLink"tools:layout="@layout/fragment_deep_link"><deepLink app:uri="example://deepLink/{id}" /></fragment></navigation>
如需启用隐式深层链接,您还必须向应用的 manifest.xml
文件中添加内容。将一个 <nav-graph>
元素添加到指向现有导航图的 activity
,如以下示例所示。
<activityandroid:name=".navigation.DeepLinkActivity"android:exported="true"><nav-graph android:value="@navigation/nav_deep_link" />
</activity>
在这个例子中,我们定义了一个深度链接,它的 URI 是"example://deepLink/{id}"
,其中{itemId}
是一个参数。当用户在浏览器或其他应用中点击这个链接时,Android 系统会自动打开我们的应用,并跳转到对应的页面,同时将参数传递给我们的应用。我们可以在目标页面中通过arguments
来获取这个参数。
3.2 共享元素转场
- 共享元素转场可以实现在不同
Activity
或Fragment
之间共享相同元素的动画效果,比如在列表页面点击某个item
进入详情页面时,可以让这个item
的图片或文字在两个页面之间平滑地过渡。以下是一个简单的实现示例:
<!-- 在layout文件中定义共享元素的id --><ImageView android:id="@+id/image_view"android:layout_width="wrap_content"android:layout_height="wrap_content"android:transitionName="shared_element" />
- 到 Fragment 目的地的共享元素过渡
val extras = FragmentNavigatorExtras(view1 to "shared_element")view.findNavController().navigate(R.id.confirmationAction, null, null, extras)
- 到 Activity 目的地的共享元素过渡
val option = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, imageView, "shared_element")findNavController().navigate(R.id.shareElementDialog, null, null, ActivityNavigatorExtras(option))
共享元素以程序化方式提供,而不是通过导航 XML 文件提供。activity
和 fragment
目的地各自都有 Navigator.Extras
接口的一个子类,它接受导航的附加选项,包括共享元素。您可以在调用 navigate()
时传递这些 Extras
。
3.3 导航图的动态构建- 动态构建导航图可以在运行时根据不同的条件创建不同的导航图,例如用户登录状态不同、权限不同等情况下展示不同的导航结构。
- 下面是一个简单的动态构建导航图的示例:
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragmentval navController = navHostFragment.navControllerval graph = navInflater.inflate(R.navigation.dynamic_nav_graph)if (isLoggedIn) {graph.startDestination = R.id.homeFragment} else {graph.startDestination = R.id.loginFragment}if (hasAdminPermissions) {val adminNode = NavGraphNavigator(navController.navigatorProvider.getNavigator(NavGraphNavigator::class.java)).createDestination()adminNode.id = R.id.adminFragmentadminNode.setClassName("com.example.app.AdminFragment")graph.addDestination(adminNode)graph.addEdge(R.id.homeFragment, R.id.adminFragment)}navController.graph = graph
在上面的示例中,我们首先获取到了当前的NavController
和NavInflater
,然后通过NavInflater.inflate
方法来加载我们的动态导航图。接着,我们根据不同的条件设置了导航图的起始目的地,并且在有管理员权限的情况下动态添加了一个目的地,并且添加了一条边来连接这个目的地和主页。最后,我们将构建好的导航图设置到NavController
中即可。
四. 导航组件的最佳实践
4.1 使用<include>
标签
- 为每个模块定义单独的
NavGraph
在大型项目中,最好为每个功能模块定义自己的NavGraph
,然后在根NavGraph
中使用<include>
标签将每个模块的NavGraph
组合起来::
<navigation xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/root_navigation"><include android:id="@+id/home_navigation"app:layout="@navigation/home_navigation" /><include android:id="@+id/profile_navigation"app:layout="@navigation/profile_navigation" /> </navigation>
4.1 使用 ViewModel 和 LiveData
- 在 ViewModel 中使用 LiveData 对象来处理导航事件:
class MainViewModel : ViewModel() {private val navigateTo = MutableLiveData<NavDirections>()fun getNavigateTo(): LiveData<NavDirections> {return navigateTo}fun setNavigateTo(directions: NavDirections) {navigateTo.value = directions}}
- 在 Fragment 中观察 LiveData 对象并处理导航事件:
class MainFragment : Fragment() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)viewModel.getNavigateTo().observe(this) {Navigation.findNavController(requireView()).navigate(it)}}fun clickJump() {viewModel.setNavigateTo(MainFragmentDirections.actionMainToNavigationActivity())}}
注意这里我们使用了 Safe Args
实现类型安全的导航,在目的地之间导航,官方也是建议使用 Safe Args Gradle
插件。此插件可生成简单的对象和构建器类,以便在目的地之间实现类型安全的导航。我们强烈建议您在导航以及在目的地之间传递数据时使用 Safe Args
。
如需将 Safe Args
添加到您的项目,请在顶层 build.gradle
文件中包含以下 classpath:
buildscript {dependencies {def nav_version = "2.5.3"classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version")}
}
然后再将以下行添加到应用或模块的 build.gradle 文件中:
plugins {id("androidx.navigation.safeargs.kotlin")
}
五. 总结
5.1 导航组件的优势和适用场景:
优势 | 适用场景 |
---|---|
1. 提供一致的导航体验 | 适用于需要在应用中引入多个页面的场景 |
2. 简化导航逻辑 | 适用于需要在应用中进行复杂的导航操作的场景 |
3. 可自定义外观和行为 | 适用于需要根据应用需求自定义导航栏的场景 |
4. 支持深层链接 | 适用于需要在应用中支持深层链接的场景 |
5.2 导航组件的最佳实践- 在使用导航组件时,应该尽量减少手动操作 Fragment 事务,而是使用导航组件提供的 API 进行操作,以避免出现不必要的错误。
- 在设计导航图时,应该尽量将功能相似的页面放在同一个导航图中,以便于管理和维护。
- 在使用 Safe Args 插件传递参数时,应该尽量使用安全的类型,以避免出现类型转换错误。
以上就是对 Android Navigation 的探索与实践的过程,上述示例 + 扩展(结合 BottomNavigationView 和 DrawerLayout)请参考 https://github.com/smallmarker/JetPackPractice
相关文章:

Android Jetpack - Navigation 组件:进行应用程序导航
一. Navigation 组件的介绍 1.1 什么是 Navigation 组件 Navigation 组件是一种 Android Jetpack 库,它可以帮助开发者轻松地实现应用程序中的导航功能。导航组件包含多个类和组件,包括导航图、目的地、导航控制器等,可以帮助我们管理应用程…...
MySQL的binlog原理和它的几种使用方法
MySQL中的二进制日志(binlog)是一种用于记录数据库操作的日志文件,它可以记录MySQL服务器接收到的所有修改数据库的语句,例如INSERT、UPDATE和DELETE等语句。二进制日志对于备份和恢复数据库、复制数据库和进行数据分析等操作非常…...
40岁以上的程序员还容易找到工作吗?聊聊我自己的亲身经历
今天我们来讨论一个比较热门的话题,那就是程序员。如果到了40岁以上还容易找到工作吗?这个问题呢,其实是一个非常现实的问题,也是我们程序员非常关心的一个问题。因为我们每一个程序员,他都会有到40岁的那一天。 首先…...

Class类
package com.hspedu.reflection.class_;import com.hspedu.Cat;import java.util.ArrayList;/*** author 韩顺平* version 1.0* 对Class类特点的梳理*/ public class Class01 {public static void main(String[] args) throws ClassNotFoundException {//看看Class类图//1. Cla…...

Python小姿势 - 可选知识点:
可选知识点: 列表推导式 列表和字典推导式 字典推导式 生成器表达式 带条件的生成器表达式 解析XML 解析JSON 使用Requests和BeautifulSoup爬虫 Python并发编程 Python多线程编程 Python多进程编程 Python异步编程 Python装饰器 Python闭包 Python模块化 Python类和…...

Javaee Spring的AOP简介
一.Spring的AOP简介 1.1 什么是AOP AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代 理实现程序功能的统一维护的一种技术。AOP 是 OOP 的延续,是软件开发中的一个热点,也是…...
基于ansible初始化linux服务器基础环境。
大家好,今天我要和大家分享一个关于搭建centos环境的新方法。 以前我们经常会看到一些文章介绍如何搭建centos环境,但很多时候都会出现一些问题。不过现在有了一种新的方法,就是使用ansible脚本来实现。 虽然这种方法仅适用于centos7&#…...
leetcode-数据库题
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 175. 组合两个表176. 第二高的薪水177. 第N高的薪水178. 分数排名181. 超过经理收入的员工182. 查找重复的电子邮箱183. 从不订购的客户 175. 组合两个表 select p…...

[元来学NVMe协议] NVMe IO 指令集(NVM 指令集)| Flush 命令
声明 主页:元存储的博客_CSDN博客 依公开知识及经验整理,如有误请留言。 个人辛苦整理,付费内容,禁止转载。 内容摘要 前言 NVMe2.0 定义的三类命令集: 管理命令集、IO命令集、Fabrics命令集 Admin Command Set (管理命令集):用于控制器的管理,如创建/销毁IO提交队列…...

信息的相关性和冗余度:信息在整个文明中的作用
文章目录 I 古埃及的象形文字1.1 罗塞塔石碑1.2 古埃及文字音节和希腊字母的对应表1.3 破解古埃及文字 I 古埃及的象形文字 1.1 罗塞塔石碑 这个石碑是在公元前196年埃及国王托勒密五世加冕一周年的诏书。 在此前大约一百年,埃及已经被来自希腊北方城邦的亚历山大…...

python数据结构与算法-动态规划(最长公共子序列)
一、最长公共子序列问题 1、问题概念 一个序列的子序列是在该序列中删去若干元素后得 到的序列。 例如:"ABCD”和“BDF”都是“ABCDEFG”的子序列。 最长公共子序列(LCS) 问题: 给定两个序列X和Y,求X和Y长度最大的公共子字列。 例:X"ABBCBDE”…...
Java版企业电子招投标系统源码 Spring Cloud+Spring Boot 电子招标采购系统功能清单
一、立项管理 1、招标立项申请 功能点:招标类项目立项申请入口,用户可以保存为草稿,提交。 2、非招标立项申请 功能点:非招标立项申请入口、用户可以保存为草稿、提交。 3、采购立项列表 功能点:对草稿进行编辑&#x…...

【c语言】函数的基本概念 | 函数堆栈调用原理
创作不易,本篇文章如果帮助到了你,还请点赞支持一下♡>𖥦<)!! 主页专栏有更多知识,如有疑问欢迎大家指正讨论,共同进步! 给大家跳段街舞感谢支持!ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ…...

Vue.prototype 详解及使用
前言: 我们可能会在很多组件里用到数据/实用工具,但是不想污染全局作用域。这种情况下,可以通过在原型上定义它们使其在每个 Vue 的实例中可用。 1. 基本示例 在main.js中添加一个变量到 Vue.prototype Vue.prototype.$appName My App这…...

音视频八股文(3)--ffmpeg常见命令(2)
07-ffplay命令播放媒体 播放本地文件 播放本地 MP4 视频文件 test.mp4 的命令,从第 2 秒位置开始播放,播放时长为 10 秒,并且在窗口标题中显示 “test time”: ffplay -window_title "test time" -ss 2 -t 10 -autoe…...

使用bert4keras出现的问题(Process finished with exit code -1073741819 (0xC0000005))
1、环境 python 3.7.12 tensorflow 1.15 keras 2.3.1 bert4keras 0.9.7 protobuf 3.19.0 numpy 1.16.5 2、出现问题 numpy版本不兼容问题所以你就直接按照我的版本就可以了(numpy 1.16.5) Process finished with exit code -1073741819 (0xC0000005) …...

python协程实战
协程简介 协程(Coroutine)又称微线程、纤程,协程不是进程或线程,其执行过程类似于 Python 函数调用,Python 的 asyncio 模块实现的异步IO编程框架中,协程是对使用 async 关键字定义的异步函数的调用; 一个进程包含多个线程,类似…...

【论文笔记】VideoGPT: Video Generation using VQ-VAE and Transformers
论文标题:VideoGPT: Video Generation using VQ-VAE and Transformers 论文代码:https://wilson1yan. github.io/videogpt/index.html. 论文链接:https://arxiv.org/abs/2104.10157 发表时间: 2021年9月 Abstract 作者提出了…...

scala之基础面向对象
scala 既是面向对象 也是函数式编程 从Java 发展而来,依赖JVM环境 一、 scala 在linux中运行 scala 模式中直接编写运行 scala文件,load执行 scala编译程序 编译 运行 scala java 二、scala 数据类型 基础数据类型 val 不可变变量 函数式编程 …...
Qt5.12实战之多线程编程概念
1.为什么要使用多线程? a. 基于线程,同时处理多个任务,软件响应更灵敏 b.充分利用CPU的多核心功能增加应用运行效率 c.多线程在同一进程间使用共享通信更加高效 d.多个线程之间进行切换比多个进程之间进行切换,线程开销更少. 2.操作系统与进程关系 a. MS-DOS系统 属于单进程…...

Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...

有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...

【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...