1.基本使用

第一步,添加依赖

Navigation的依赖添加:

dependencies {def nav_version = "2.5.1"// Java language implementationimplementation "androidx.navigation:navigation-fragment:$nav_version"implementation "androidx.navigation:navigation-ui:$nav_version"// Kotlinimplementation "androidx.navigation:navigation-fragment-ktx:$nav_version"implementation "androidx.navigation:navigation-ui-ktx:$nav_version"// Feature module Supportimplementation "androidx.navigation:navigation-dynamic-features-fragment:$nav_version"// Testing NavigationandroidTestImplementation "androidx.navigation:navigation-testing:$nav_version"// Jetpack Compose Integrationimplementation "androidx.navigation:navigation-compose:$nav_version"
}

为了方便在fragment之间传递参数,同时添加Safe Args的依赖,也可以选择不添加,使用Bundle进行fragment间的传递。

Safe Args依赖添加:

顶层 build.gradle 文件中包含以下 classpath

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

添加插件,选择其一

plugins {//适用于 Java 模块或 Java 和 Kotlin 混合模块的 Java 语言代码id 'androidx.navigation.safeargs'//仅适用于 Kotlin 模块的 Kotlin 语言代码// id 'androidx.navigation.safeargs.kotlin'
}

第二步,创建导航宿主Activity以及需要的Fragment

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

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

注意:Navigation 组件旨在用于具有一个主 Activity 和多个 Fragment 目的地的应用。主 Activity 与导航图相关联,且包含一个负责根据需要交换目的地的 NavHostFragment。在具有多个 Activity 目的地的应用中,每个 Activity 均拥有其自己的导航图。

核心部分在于Activity的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"xmlns:app="http://schemas.android.com/apk/res-auto"><fragmentandroid:id="@+id/nav_host_fragment"android:name="androidx.navigation.fragment.NavHostFragment"android:layout_width="match_parent"android:layout_height="match_parent"app:defaultNavHost="true"app:navGraph="@navigation/nav_graph"/>
</FrameLayout>

Activity中如无业务需求,那么一般无需在Activity添加对Navigation的操作。

注意点

android:name 属性包含 NavHost 实现的类名称。

app:navGraph 属性将 NavHostFragment 与导航图相关联。导航图会在此 NavHostFragment 中指定用户可以导航到的所有目的地。

app:defaultNavHost=“true” 属性确保您的 NavHostFragment 会拦截系统返回按钮。请注意,只能有一个默认 NavHost。如果同一布局(例如,双窗格布局)中有多个宿主,请务必仅指定一个默认 NavHost。

第三步,创建导航图

导航图是什么呢?在Navigation中,它是包含您的所有目的地和操作的一种资源文件。在Android studio可视化后,如下图所示:

其中,我们可以看到,里面由不同的界面和一系列的箭头连线组成。界面一般是Fragment,连线则是Fragment之间的跳转关系。其中有些连线还有一些向上的箭头,这个是表示在这次跳转中有fragment存在出栈的操作。

查看导航图的代码:

我们从中一步步分析往下。

首先,顶部的是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"android:id="@+id/nav_graph"><!--省略...-->
</navigation>

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

第四步,创建目的地

目的地,就是导航图中的一个个的界面,在Navigation中可以是Fragment,Activity,DialogFragment等…

这里我们主要介绍Fragment目的地。

在navigation标签下添加fragment标签,如下所示:

<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/navigation"app:startDestination="@id/AllInfoFragment"><fragmentandroid:id="@+id/BaseInfoFragment"android:name="xxx.xxx.xxx.BaseInfoFragment"android:label="BaseInfoFragment"tools:layout="@layout/fragment_base_info"></fragment></navigation>
  • Label 字段包含该目的地的用户可读名称。例如,如果您使用 setupWithNavController() 将 NavGraph 连接到 Toolbar,就可能在界面上看到此字段。因此,我们建议您对此值使用资源字符串。
  • ID 字段包含该目的地的 ID,它用于在代码中引用该目的地。
  • Name 关联的类的名称。
  • Tools:layout可视化中显示的视图,对运行代码无影响
  • Navigation->startDestination 开始的目的地或者说该Navigation第一个页面

第五步,创建跳转

在navigation中跳转由实现,是每个目的地之间的逻辑连接,Action通常会将一个目的地连接到另一个目的地,当然也有全局Action,此类Action可让您从应用中的任意位置转到特定目的地。

这里我们主要介绍普通的Action。

<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/navigation"app:startDestination="@id/AllInfoFragment"><fragmentandroid:id="@+id/BaseInfoFragment"android:name="xxx.xxx.xxx.BaseInfoFragment"android:label="BaseInfoFragment"tools:layout="@layout/fragment_base_info"><actionandroid:id="@+id/action_BaseInfo_to_AllInfo"app:destination="@id/AllInfoFragment"app:popUpTo="@id/BaseInfoFragment"app:popUpToInclusive="true"app:launchSingleTop="true"/></fragment><fragmentandroid:id="@+id/AllInfoFragment"android:name="xxx.xxx.xxx.AllInfoFragment"android:label="AllInfoFragment"tools:layout="@layout/fragment_all_info"></fragment>
</navigation>

Action会包含以下参数。

  • ID 字段包含该操作的 ID。
  • Destination 字段包含目的地 Fragment 或 Activity 的 ID。
  • popUpTo 表示跳转到某个tag,并将tag之上的元素出栈。
  • popUpToInclusive 为true表示会弹出tag,false则不会。
  • launchSingleTop 如果栈中已经包含了指定要跳转的界面,那么只会保留一个,不指定则栈中会出现两个界面相同的Fragment数据,可以理解为类似activity的 singleTop,即栈顶复用模式

详情:参考链接

调用:

val navHostFragment =supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
navController.navigate(R.id.action_BaseInfo_to_AllInfo)//简化版本
findNavController().navigate(R.id.action_BaseInfo_to_AllInfo)

第六步,传递参数

使用Safe Args进行传参,如果不使用的话,那么可以用Bundle代替。这里可以跳过。

示例代码:

<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/navigation"app:startDestination="@id/AllInfoFragment"><fragmentandroid:id="@+id/BaseInfoFragment"android:name="xxx.xxx.xxx.BaseInfoFragment"android:label="BaseInfoFragment"tools:layout="@layout/fragment_base_info"><actionandroid:id="@+id/action_BaseInfo_to_DynamicParameter"app:destination="@id/DynamicParameterFragment" /></fragment><fragmentandroid:id="@+id/DynamicParameterFragment"android:name="xxx.xxx.xxx.DynamicParameterFragment"android:label="DynamicParameterFragment"tools:layout="@layout/fragment_dynamic_parameter"><argumentandroid:name="navigationParameterId"app:argType="integer"android:defaultValue="-1"/><argumentandroid:name="navigationParameterName"app:argType="string"app:nullable="true"/><argumentandroid:name="navigationFromAllInfo"app:argType="boolean"app:nullable="false"/></fragment></navigation>

简单的参数如上所示,由argument标签修饰,

  • name 参数名。
  • argType 参数类型。
  • nullable 可否为null。

支持的参数类型如下:

类型 语法 是否支持默认值 是否由路由处理 是否允许为null
整数 app:argType=“integer”
浮点数 app:argType=“float”
长整数 app:argType=“long” 是 - 默认值必须始终以“L”后缀结尾(例如“123L”)。
布尔值 app:argType=“boolean” 是 -“true”或“false”
字符串 app:argType=“string”
资源引用 app:argType=“reference” 是 - 默认值必须为“@resourceType/resourceName”格式(例如,“@style/myCustomStyle”)或“0”
自定义Parcelable app:argType=“”,其中 是 Parcelable 的完全限定类名称 支持默认值“@null”。 不支持其他默认值。
自定义Serializable app:argType=“”,其中 是 Serializable 的完全限定类名称 支持默认值“@null”。 不支持其他默认值。
自定义Enum app:argType=“”,其中 是 Enum 的完全限定名称 是 - 默认值必须与非限定名称匹配(例如,“SUCCESS”匹配 MyEnum.SUCCESS)。

如果参数类型支持 null 值,您可以使用 android:defaultValue=“@null” 声明默认值 null。

替换操作中的目的地参数

所有导航至目的地的操作都使用目的地级的参数和默认值。如果需要,您可以通过在操作级定义参数来替换参数的默认值(如果不存在,则设置一个)。此参数必须与目的地中声明的参数具有相同的名称和类型。

以下 XML 展示了如何声明操作并替换上例中的目的地级参数:

<action android:id="@+id/startMyFragment"app:destination="@+id/myFragment"><argumentandroid:name="myArg"app:argType="integer"android:defaultValue="1" />
</action>

启用 Safe Args 后,生成的代码会为每个操作包含以下类型安全的类和方法,以及每个发送和接收目的地。

为生成操作的每一个目的地创建一个类。该类的名称是在源目的地的名称后面加上“Directions”。例如,如果源目的地是名为 SpecifyAmountFragment 的 Fragment,则生成的类的名称为 SpecifyAmountFragmentDirections。

该类会为源目的地中定义的每个操作提供一个方法。

对于用于传递参数的每个操作,都会创建一个 inner 类,该类的名称根据操作的名称确定。例如,如果操作名称为 confirmationAction,,则类名称为 ConfirmationAction。如果您的操作包含不带 defaultValue 的参数,则您可以使用关联的 action 类来设置参数值。

为接收目的地创建一个类。该类的名称是在目的地的名称后面加上“Args”。例如,如果目的地 Fragment 的名称为 ConfirmationFragment,,则生成的类的名称为 ConfirmationFragmentArgs。可以使用该类的 fromBundle() 方法检索参数。

所以以上代码跳转到DynamicParameterFragment并附带参数的写法就如下所示:

BaseInfoFragmentDirections.actionBaseInfoToDynamicParameter(list[0].category_id,list[0].category_name,false)

官方链接:https://developer.android.google.cn/guide/navigation?hl=zh-cn

2.踩坑记录

第一点.navigation下的fragment声明周期

Navigation框架下的Fragment压栈时View会被销毁,再进入栈顶时会再次构建View。这就面临一个Activity不会存在的问题:如何恢复View的数据? 例如:常见的RecyclerView也会在入栈的时候被销毁,当用户返回时候,需要重新构建RecyclerView、绑定Adapter, 因此Adapter或者说是列表数据不能丢失,需要在ViewModel或者Fragment类成员变量中保存, 否则就只能再次请求网络加载数据(体验非常糟糕)。Fragment和View分家的问题,用下面的2张生命周期图可以阐释出来:

Navigation出现之前官方给出的Fragment生命周期如下图:(注意onDestroyView之处)


而LIfecycle,Navigation等组件出现之后,官方给出的Fragment生命周期图为下图:

Navigation框架下的Fragment生命周期分为 Fragment Lifecycle 和 View Lifecycle ,View Lifecycle被单独拎出来了,原因就在于Navigation框架下的非栈顶的Fragment均会被销毁View, 也即是 A跳转到B页面: A会执行onDestroyView销毁其 View (凡是和View相关的,如:Databinding、RecyclerView都会被销毁) , 但是Fragment本身会存在( Fragment本身的成员变量等 是不会被销毁的 ) 。为啥这样设计, 请参考Navigation的这个 Issue:Navigation, Saving fragment state , 很多人重写Navigation,使其能够保存View。这个例子请参考 起初Jetpack Navigation把我逼疯了,可是后来真香 ,大致的实现就是将官方的replace方式替换为Hide和Show。 但是不建议。

Navigation框架之下的正确状态流转应该是类似这的:

A 通过action打开B,A从 onResume转到onDestroyView,B从onAttach执行到onResume, 当B通过系统返回键返回到A时候,A从上图的onCreateView流转到onResume , 此过程中A的View经历销毁和重建,View(binding实例)的对象实例是不一样的,但是Fragment A这个实例始终相同。

这样的场景下,假设A存在一个网络新闻列表RecyclerView, RecyclerView随着View被销毁、重建。 如何保存其中的数据,避免每次返回到A的时候重新刷新数据(造成:上次浏览数据、位置丢失、额外的网络资源消耗), 因此RecyclerView中Adapter的数据项非常关键! 常见的保存方式有: 1、通过Fragment的成员变量 2、ViewModel 。方法2非常合适,在ViewModel的ViewModelScope通过协程请求网络数据,保存在ViewModel(ViewModel生命周期贯穿Fragment),可通过LiveData、普通变量保存数据,在 onViewCreated之后恢复数据。

参考链接:http://www.icodebang.com/article/274205

第二点.返回键监听

在业务处理中,经常在一些需要编辑的界面对返回键进行监听,比如当用户编辑了某一内容的时候,需要返回键进行拦截添加一些挽留的Dialog弹窗。

在fragment中我们可以使用如下方法实现。

//返回按钮拦截件监听
val callback: OnBackPressedCallback = object : OnBackPressedCallback(true){override fun handleOnBackPressed() {quitLogic()}
}
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner,callback)

第三点.Navigation中的返回栈

一开始由于对popUpTo参数的理解存在偏差,导致Navigation的返回栈不及预期。这里需要重点把控一些,Navigation在2.3.0版本后就没有了对返回栈清空的操作,所以在2.3.0后需要结合popUpTo与popUpToInclusive来实现清空栈的操作。

popUpTo表示会将返回栈中某个tag的以上的元素出栈。

popUpToInclusive为true表示会弹出tag,false则不会。

第四点.声明周期引发的一系列问题

参考:https://mp.weixin.qq.com/s/XdPYjC_6NP-0rmp8Y3hNpg

评论回复不及时,有问题的小伙伴可以点击链接:Android 学习交流 提问

Navigation 的应用实践相关推荐

  1. GMTC 大前端时代前端监控的最佳实践

    摘要: 今天我分享的内容分成三个部分: 第一部分是"大前端时代前端监控新的变化", 讲述这些年来,前端监控一些新的视角以及最前沿的一些思考. 第二部分"前端监控的最佳实践 ...

  2. 前端代码标准最佳实践:CSS篇

    上一篇<前端代码标准最佳实践:javascript>发表后,大家讨论还是很热烈,从侧面体现了前端工程师对写标准的前端代码的重视程度很高.这些最佳标准实践并不是那个权威组织发布的,而是由大量 ...

  3. Android Jetpack组件之Navigation使用-源码

    1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面. A ...

  4. UGUI全面实践教程

     UGUI全面实践教程  试读文档下载地址:http://pan.baidu.com/s/1hq3UYGk 介绍:UGUI是Unity官方推出的最新UI系统.本教程为国内唯一的UGUI专向资料.本教程 ...

  5. 定位系列论文阅读-RoNIN(二)-Robust Neural Inertial Navigation in the Wild: Benchmark, Evaluations

    这里写目录标题 0.Abstract 0.1逐句翻译 0.2总结 1. Introduction 1.1逐句翻译 第一段(就是说惯性传感器十分重要有研究的必要) 第二段(惯性导航是非常理想的一个导航方 ...

  6. 向256 MB内存的Windows Phone提供应用的最佳实践指导

    简介 为了使得应用能在256 MB的Windows Phone设备上运行需要进行一些改动. 首先,与512 MB的设备相比,256 MB手机上的内存使用/分配方式是不同的.运行在256 MB上的应用仍 ...

  7. 内置host_浏览器内置对象应用实践

    " 浏览器内置对象,我们每天都与其打交道,可能你并未全面的去了解过它.我们一起讨论下你有过哪些实践." 1. 什么是浏览器对象模型?(BOM :Browser Object Mod ...

  8. 利用Navigation Timing测量页面加载时间

    最近在看一本名为<web性能实践日志>的书籍,其中第十三章"网络计时"中介绍了一种比较新的计算页面各部分加载时间方法,这也是W3C Web性能工作小组正在做的事情,接下 ...

  9. 《推荐系统实践》附上Reference 中的干货 (Paper,Blog等资料的链接)

    <推荐系统实践>这只是一本197页的书,但作者附上了诸多好资料,无论是paper, blog文章,wikipedia词条,数据集还是开源项目等 附上资料链接,格式基本按照'URL+资料名称 ...

最新文章

  1. firefox显示nagios
  2. 模拟海_浙江平湖“海水稻”丰收的背后,有这家来自上海企业的科技“秘方”...
  3. jquery 轮播插件 bxslider
  4. 【C 语言】二级指针内存模型 ( 指针数组 | 二维数组 | 自定义二级指针 | 将 一、二 模型数据拷贝到 三 模型中 并 排序 )
  5. (1) redis windows安装
  6. 计算机原理与应用简称,基础知识-计算机原理与应用.ppt
  7. CISO的真正挑战:密码管理、IoT安全合规性
  8. CentOS7 安装Redis Cluster集群
  9. 图形化界面工具 SQLyog
  10. HTML5网站大观:分享8个精美的 HTML5 网站案例
  11. 全网首发:(解决办法)MAC OS Xcode给应用设置沙箱(Enable App Sandbox)之后,运行报错Illegal instruction: 4
  12. 9 ASCLL 码表
  13. 【系统分析师】考试内容大纲内容
  14. 解决python处理word文档时的两个问题,截取内容和页眉横线
  15. 哈哈哈哈,IT总监VS美发总监
  16. 赛博僵尸道长 v1.2
  17. jfinal与bootstrap的登出实战
  18. 预估期刊2021年的影响因子
  19. 新华三智慧计算联盟,集结!
  20. error C2065: “pair”: 未声明的标识符

热门文章

  1. 20款最为知名的开源PHP CMS
  2. Docker-SaltStack-Foreman-Puppet一体化安装说明
  3. MicroPhenoDB量化关联宏基因组与病原微生物、核心基因和人类疾病表型
  4. smokeping使用
  5. java 认证_Java认证:认证或不认证
  6. Min-Max Max-Min problem algorithm and analysis
  7. 智慧实验室安全管理系统都可以管什么?
  8. 让微信二维码扫描您的APK
  9. 2019通信展共建万物互联 百卓网络赋能5G网络安全
  10. 2021年美容师(中级)考试题库及美容师(中级)作业模拟考试