Android Jetpack - Navigation 组件:进行应用程序导航

news2024/11/21 0:13:50

在这里插入图片描述

一. 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.FragmentContainerView
    android: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页面之间的导航关系和跳转路径。

NavControllerNavigation组件的控制中心,用于在NavHost内执行导航操作。可以在ActivityFragment中通过NavHostFragmentnavController属性获取对应的NavController实例。

常见的导航操作有:

  • 导航到目标目的地:navController.navigate(R.id.destination_id)
  • 回退一个目的地:navController.navigateUp()navController.popBackStack()
  • 回退到根目的地:navController.popBackStack(R.id.root_destination, false)

NavController还负责维护Fragment的回退栈,以及在按返回按钮时正确出栈,这大大简化了之前管理Fragment事务的复杂度。
通过NavHostNavController的配合,Navigation组件实现了在NavGraph中声明的导航逻辑和页面切换功能。这使Fragment之间的导航变得极为简单高效。开发者只需关注于定义NavGraph,并调用NavController中的导航方法即可实现页面跳转,其余的一切尽在Navigation组件的掌控之中。

2.2 创建导航图

  • 在XML文件中创建导航图:
<androidx.fragment.app.FragmentContainerView
    android: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">

    <fragment
        android:id="@+id/firstFragment"
        android:name="com.smallmarker.jetpackpractice.navigation.fragment.FirstFragment"
        android:label="First"
        tools:layout="@layout/fragment_first">
        <action
            android:id="@+id/action_firstFragment_to_secondFragment"
            app:destination="@+id/secondFragment" />
    </fragment>

    <fragment
        android: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">
  
    <fragment
        android: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,如以下示例所示。

<activity
    android:name=".navigation.DeepLinkActivity"
    android:exported="true">
    <nav-graph android:value="@navigation/nav_deep_link" />
</activity>

在这个例子中,我们定义了一个深度链接,它的 URI 是"example://deepLink/{id}",其中{itemId}是一个参数。当用户在浏览器或其他应用中点击这个链接时,Android 系统会自动打开我们的应用,并跳转到对应的页面,同时将参数传递给我们的应用。我们可以在目标页面中通过arguments来获取这个参数。

3.2 共享元素转场

  • 共享元素转场可以实现在不同ActivityFragment之间共享相同元素的动画效果,比如在列表页面点击某个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 文件提供。activityfragment 目的地各自都有 Navigator.Extras 接口的一个子类,它接受导航的附加选项,包括共享元素。您可以在调用 navigate() 时传递这些 Extras

3.3 导航图的动态构建- 动态构建导航图可以在运行时根据不同的条件创建不同的导航图,例如用户登录状态不同、权限不同等情况下展示不同的导航结构。

  • 下面是一个简单的动态构建导航图的示例:
  val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
  val navController = navHostFragment.navController
  val 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.adminFragment
      adminNode.setClassName("com.example.app.AdminFragment")
      graph.addDestination(adminNode)
      graph.addEdge(R.id.homeFragment, R.id.adminFragment)
  }
  
  navController.graph = graph

在上面的示例中,我们首先获取到了当前的NavControllerNavInflater,然后通过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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/450662.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Node.JS Web编程】记录从语法基础到网络框架的学习过程

文章目录 1. Node.JS 模块系统2. npm使用介绍3. 搭建第一个服务端应用4. GET / POST请求5. Web 前后端分离6. 使用Express框架搭建Web服务7. Request 和 Response8. 中间件9. 使用 Koa 框架搭建Web服务10. 使用 Egg 框架搭建Web服务11. egg 项目结构大全 注意&#xff1a;本次教…

【Linux】uptime命令详解平均负载

命令 ➜ ~ uptime 22:37 up 90 days, 21:45, 2 users, load averages: 2.91 3.46 3.81 具体含义 22:37&#xff1a;代表的是当前的系统时间&#xff0c;也即晚上10点37分。 up 90 days, 21:45&#xff1a;代表系统运行时间 2 users &#xff1a;当前两个用户 load averages: 2…

【Linux】命名管道使用示例-代码实现

文章目录 1 管道基础知识复习(可直接跳转代码实现)1.1 管道的读写规则1.2 管道的特点 2 命名管道2.1 命名管道本质2.2 创建命名管道2.2.1 在命令行创建:2.2.2 在程序中调用函数创建 2.3 命名管道和匿名管道的区别2.4 命名管道的打开规则 3 代码分解实现3.1 makefile书写3.1.1 测…

计算值组成原理 作业8

作业8 题量: 28 满分: 100 作答时间:04-20 09:40至04-26 23:59 100分 一. 单选题&#xff08;共14题&#xff0c;32分&#xff09; 1. (单选题, 2分)计算机硬件能直接执行的只有_____。 A. 算法语言B. 汇编语…

2023-04-22 学习记录--C/C++-数组

合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 一、定义一维数组 ⭐️ &#xff08;一&#xff09;、初识 格式 &#x1f308;&#xff1a;数组元素类型 数组名[数组元素个数]…

Java每日一练(20230423)

目录 1. 数组元素统计 ※ 2. 杨辉三角 II &#x1f31f; 3. 二进制求和 &#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 1. 数组元素统计 定义一个长度为5的数组arr1&a…

【三十天精通Vue 3】第十六天 Vue 3 的虚拟 DOM 原理详解

引言 Vue 3 的虚拟 DOM 是一种用于优化 Vue 应用程序性能的技术。它通过将组件实例转换为虚拟 DOM&#xff0c;并在组件更新时递归地更新虚拟 DOM&#xff0c;以达到高效的渲染性能。在 Vue 3 中&#xff0c;虚拟 DOM 树由 VNode 组成&#xff0c;VNode 是虚拟 DOM 的基本单元…

PTA L1-096 谁管谁叫爹 (20 分)

《咱俩谁管谁叫爹》是网上一首搞笑饶舌歌曲&#xff0c;来源于东北酒桌上的助兴游戏。现在我们把这个游戏的难度拔高一点&#xff0c;多耗一些智商。 不妨设游戏中的两个人为 A 和 B。游戏开始后&#xff0c;两人同时报出两个整数 N A N_A NA​​ 和 N B ​ N_B​ NB​​ 。判…

C语言函数大全-- n 开头的函数

C语言函数大全 本篇介绍C语言函数大全-- n 开头的函数 1. nan 1.1 函数说明 函数声明函数功能double nan(const char *tagp);用于返回一个表示 NaN&#xff08;非数值&#xff09;的 double 类型数字 参数&#xff1a; tagp &#xff1a; 指向字符串的指针&#xff1b;用于…

Tomcat 配置与部署

http 协议就是 http 客户端和 http 服务器之间通信的协议 , 而Tomcat 就是 java 圈子中最广泛使用的 http 服务器. 下载Tomcat Tomcat官网 Tomcat 的版本 , 和后续的 servlet 版本是强相关的 , 此处使用 tomcat 8 , 对应的 servlet 就是 3.1 下载一个 zip 压缩包解压缩即可 T…

探索【Stable-Diffusion WEBUI】的插件:骨骼姿态(OpenPose)

文章目录 &#xff08;零&#xff09;前言&#xff08;一&#xff09;骨骼姿态&#xff08;OpenPose&#xff09;系列插件&#xff08;二&#xff09;插件&#xff1a;PoseX&#xff08;三&#xff09;插件&#xff1a;Depth Lib&#xff08;四&#xff09;插件&#xff1a;3D …

Spring之IOC和DI入门案例

IOC和DI入门案例 1. IOC入门案例1.1 门案例思路分析1.2 实现步骤1.3 实现代码1.4 运行结果 2. DI入门案例2.1 DI入门案例思路分析2.2 实现步骤2.3 实现代码2.4 图解演示 1. IOC入门案例 问题导入 <bean>标签中id属性和class属性的作用是什么&#xff1f; 1.1 门案例思…

金三银四总计面试碰壁15次,作为一个27岁的测试工程师.....

3年测试经验原来什么都不是&#xff0c;只是给你的简历上画了一笔&#xff0c;一直觉得经验多&#xff0c;无论在哪都能找到满意的工作&#xff0c;但是现实却是给我打了一个大巴掌&#xff01;事后也不会给糖的那种... 先说一下自己的个人情况&#xff0c;普通二本计算机专业…

023:Mapbox GL加载mp4视频文件

第023个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中加载MP4视频文件。一个视频源。 “urls”值是一个数组。 对于数组中的每个 URL,将创建一个视频元素源。 要支持跨浏览器的视频,请提供多种格式的 URL。“坐标”数组包含按顺时针顺序列出的视频角的 [longi…

【MYSQL】数据库和表的基本操作

目录 1.mysql的工作图&#xff1a; 2.连接mysql服务器 3.mysql的配置文件 4.数据库的操作 5.表的操作 1.mysql的工作图&#xff1a; mysql是一个应用层服务&#xff0c;需要使用安装的mysql客户端&#xff08;也叫mysql&#xff09;连接mysql服务器&#xff08;也叫mysq…

Sa-Token源码简单阅读

一.权限登录模块包括几个基本子模块&#xff1a; 1.登录。 实现方式大致为&#xff1a;先检验用户名密码是否正确&#xff0c;如正确则在缓存中存入用户信息&#xff08;一般必须要有用户标识和访问token&#xff0c;或再加一些附加信息如用户的角色权限&#xff09;&#xf…

国内外4款主流ERP系统评测,哪款最好用?

一、ERP系统的概念 ERP系统&#xff0c;是针对通用各个企业特点研发的ERP软件。由于行业产品结构复杂&#xff0c;导致原料种类众多&#xff0c;制造工艺复杂&#xff0c;外加客户、供应商、物流等不确定因素&#xff0c;传统手工、表格、纸质作业模式难以应对复杂状况&#x…

设计模式之责任链模式(C++)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 一、责任链模式是什么&#xff1f; 责任链模式是一种行为型的软件设计模式&#xff0c;对象内存在对下家的引用&#xff0c;层层连…

【世界读书日】2023年通信好书推荐

今天是世界读书日&#xff08;4月23日&#xff09;。按照老规矩&#xff0c;小编给大家推荐一些通信类的优秀书籍。 过去一年&#xff0c;通信行业的关注热点&#xff0c;主要是&#xff1a;5G-Advanced&#xff08;5.5G&#xff09;、算力网络、东数西算、6G、卫星互联网、智…

历史上的今天大事件查询工具推荐 - 历史上的今天 API

引言 历史上的今天&#xff0c;总会有一些特别的事件发生&#xff0c;这些事件对人类的发展产生了深远的影响。想要了解这些事件&#xff0c;往往需要花费大量的时间和精力去查阅历史资料。但现在&#xff0c;有了历史上的今天 API&#xff0c;一切变得方便了许多。 如果你对…