Navigation的用法
一.Navigation的诞生
单个Activity嵌套多个Fragment的UI架构模式,已经被大多数的Android工程师所接受和采用。但是,对Fragment的管理一直是一件比较麻烦的事情。我们需要通过FragmentManager和FragmentTransaction来管理Fragment之间的切换。页面的切换通常还包括对应用程序App bar的管理,Fragment之间的切换动画以及Fragment之间的参数传递。纯代码的方式使用起来不是特别友好,并且Fragment和App bar在管理和使用的过程中显得很混乱。
为此,Jetpack提供了一个名为Navigation的组件,旨在方便我们管理页面和App bar。它具有以下优势:
1.可视化的页面导航图,便于我们理清页面间的关系
2.通过destination和action完成页面间的导航
3.方便添加页面的切换动画
4.页面间类型安全的参数传递
5.通过NavigationUI类,对菜单,底部导航,抽屉菜单导航进行统一的管理
6.支持深层链接DeepLink
二.Navigation的主要元素
在正式学习Navigation之前,我们先要对Navigation中的主要元素有一个大致的了解。
Navigation Graph:导航图,包括应用程序所有的页面以及页面间的关系
NavHostFragment:这是一个特殊的Fragment,你可以认为它是其他Fragment的容器,Navigation Graph中的Fragment正是通过NavHostFragment进行展示的
NavController:导航控制器,用于在代码中完成Navigation Graph中具体的页面切换动作
它们三者之间的关系可以通过下面的这段话来理解:当你想要切换Fragment时,使用NavController对象,告诉它你想要去Navigation Graph中的哪个Fragment,NavController会将你想去的Fragment展示在NavHostFragment中。
三.如何使用Navigation
使用Navigation组件前,先要添加以下依赖:
implementation "androidx.navigation:navigation-fragment:2.5.2"
implementation "androidx.navigation:navigation-ui:2.5.2"
1.创建Navigation Graph
新建一个项目,然后在res文件夹下新建一个navigation资源目录,如下图所示:

然后在navigation目录下新建一个Navigation Resource File,名字任取,如下图所示:

2.添加NavHostFragment
NavHostFragment是一个特殊的Fragment,我们需要将它添加到Activity的布局文件中,作为其他Fragment的容器,代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">
<fragment
android:id="@+id/nav_host_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="androidx.navigation.fragment.NavHostFragment" //指定这个Fragment是一个特殊的Fragment,是其他Fragment的容器
app:defaultNavHost="true" //该Fragment会自动处理系统返回键,当用户按下返回键时,系统自动将当前所展示的Fragment退出
app:navGraph="@navigation/nav_graph"/> //用于设置该容器对应的导航图
</RelativeLayout>
此时,打开nav_graph.xml的design面板,可以看到下面的内容:

3.创建destination
单击上图中的加号按钮,然后再点击create new destination即可创建新的Fragment,destination代表目的地,就是你想去的页面。这里我们创建了MainFragment,还有对应的布局文件fragment_main.xml,此时可以看到AS为我们自动生成的代码如下:
<?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/mainFragment"> //这句代码表示默认展示的页面是MainFragment
<fragment
android:id="@+id/mainFragment"
android:name="com.example.navigation.MainFragment"
android:label="fragment_main"
tools:layout="@layout/fragment_main" >
</fragment>
</navigation>
4.完成Fragment页面的切换
我们需要再次创建一个Fragment来完成这个动作,这里我创建了SecondFragment,方式和之前创建MainFragment一样。此时,我们可以看到design面板如下所示:

我们需要拖动鼠标从mainFragment到secondFragment,之后会生成如图所示的箭头,然后切换到Code面板,可以看到生成了以下代码:
<?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/mainFragment">
<fragment
android:id="@+id/mainFragment"
android:name="com.example.navigation.MainFragment"
android:label="fragment_main"
tools:layout="@layout/fragment_main" >
<action
android:id="@+id/action_mainFragment_to_secondFragment"
app:destination="@id/secondFragment" />
</fragment>
<fragment
android:id="@+id/secondFragment"
android:name="com.example.navigation.SecondFragment"
android:label="fragment_second"
tools:layout="@layout/fragment_second" />
</navigation>
可以看到mainFragment处生成了一个action标签,表示mainFragment的目的地是secondFragment。
5.使用NavController完成导航
经过以上的步骤后,我们还需要通过NavController对象,在代码中完成具体的页面跳转工作,我们需要在MainFragment的布局文件中添加一个Button,用于页面的跳转。有两种方式可以实现页面的跳转,下面分别给出:
方法一:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view=inflater.inflate(R.layout.fragment_main,container,false);
btn_jump=view.findViewById(R.id.btn_jump);
btn_jump.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Navigation.findNavController(view).navigate(R.id.action_mainFragment_to_secondFragment);//从mainFragment到secondFragment
}
});
return view;
}
方法二:
btn_jump=view.findViewById(R.id.btn_jump);
btn_jump.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_mainFragment_to_secondFragment));
运行应用程序,然后点击按钮,可以看到页面跳转到了secondFragment,但是切换没有动画效果,显得很生硬,下面我们添加一个淡入淡出效果:
6.添加动画效果
首先,在res目录下新建一个anim文件夹,然后在这个文件夹下添加淡入淡出动画文件,代码如下:
//fade_in.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="3000" />
</set>
//fade_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromAlpha="1.0" android:toAlpha="0.0"
android:duration="3000" />
</set>
然后,修改nav_graph.xml文件,需要添加的代码如下:
<action
android:id="@+id/action_mainFragment_to_secondFragment"
app:destination="@id/secondFragment"
app:enterAnim="@anim/fade_in"
app:exitAnim="@anim/fade_out"
app:popEnterAnim="@anim/fade_in"
app:popExitAnim="@anim/fade_out" />
重新运行项目,就可以看到跳转和返回都有了淡入淡出效果。
四.使用safe args插件传递参数
在使用这个插件前,需要在project下的build.gragle文件中添加以下代码:
buildscript {
dependencies {
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.5.2"
}
}
然后,在app下的build.gradle文件中引用这个插件,需要添加的代码如下:
plugins {
id 'com.android.application'
id 'androidx.navigation.safeargs'
}
接下来,就可以使用这个插件来传递参数了。有两种方式,一种是代码的方式,一种是直接通过design面板来添加。

单击Arguments右边的加号就可以添加参数了,添加之后自动生成的代码如下:
<fragment
android:id="@+id/mainFragment"
android:name="com.example.navigation.MainFragment"
android:label="fragment_main"
tools:layout="@layout/fragment_main" >
<action
android:id="@+id/action_mainFragment_to_secondFragment"
app:destination="@id/secondFragment"
app:enterAnim="@anim/fade_in"
app:exitAnim="@anim/fade_out"
app:popEnterAnim="@anim/fade_in"
app:popExitAnim="@anim/fade_out" />
<argument
android:name="username"
app:argType="string"
android:defaultValue="unknown" />
<argument
android:name="age"
app:argType="integer"
android:defaultValue="0" />
<deepLink app:uri="http://test.com"/>
</fragment>
做完以上操作后,就可以看到safe args插件给我们生成的代码文件了,如下图所示:

可以看到,为我们生成了MainFragmentArgs.java和MainFragmentDirections.java文件。如果没有的话,可以重新编译一下项目。
然后,我们就可以利用所生成的代码文件,在Fragment之间进行参数的传递了,代码如下:
//MainFragment
@Override
public void onClick(View view) {
Bundle bundle=new MainFragmentArgs.Builder()
.setUsername("jack")
.setAge(25)
.build()
.toBundle();
Navigation.findNavController(view).navigate(R.id.action_mainFragment_to_secondFragment,bundle);
}
//SecondFragment
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle=getArguments();
if(bundle!=null){
String username = MainFragmentArgs.fromBundle(bundle).getUsername();
int age = MainFragmentArgs.fromBundle(bundle).getAge();
Log.i("username",username);
Log.i("age",age+"");
}
}
Navigation 组件具有一个名为 Safe Args 的 Gradle 插件,该插件可以生成简单的 object 和 builder 类,以便以类型安全的方式浏览和访问任何关联的参数。我们强烈建议您将 Safe Args 用于导航和数据传递,因为它可以确保类型安全。这是Android Studio官网的原话。
五.NavigationUI的使用方法
在页面的切换过程中,通常还伴随着App bar中menu菜单的变化,对于不同的页面,App bar中的menu菜单很可能是不一样的。App bar中各种按钮和菜单,同样承担着页面切换的工作。例如,当ActionBar左边的返回按钮被单击时,我们需要响应该事件,返回到上一个页面。既然Navigation和App bar都需要处理页面切换事件,那么为了方便管理,Jetpack引入了NavigationUI组件,使App bar中的按钮和菜单能够与导航图中的页面关联起来。
假设,我们有两个页面:MainFragment和SecondFragment,这两个页面同属于MainActivity。我们希望MainFragment的ActionBar右边有一个按钮,通过该按钮可以跳转到SecondFragment。而在SecondFragment的ActionBar左侧有一个返回按钮,通过该按钮,可以返回MainFragment。我们可以通过下面的方式实现:
我们在res下新建一个menu菜单,然后添加一个menu_settings.xml文件,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/secondFragment"
android:title="第二页面"/>
</menu>
需要注意的是,item标签中的id需要和导航图nav_graph.xml中SecondFragment的id一样,这表示,当该item被单击时,将会跳转到该id所对应的Fragment页面中。
在MainActivity中实例化该菜单,代码如下:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_settings,menu);
return super.onCreateOptionsMenu(menu);
}
将App bar和NavController绑定起来,代码如下:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
NavController navController= Navigation.findNavController(this,R.id.nav_host_fragment);
navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {//处理页面切换事件
@Override
public void onDestinationChanged(@NonNull NavController navController, @NonNull NavDestination navDestination, @Nullable Bundle bundle) {
switch(navDestination.getId()){
case R.id.mainFragment:
Toast.makeText(MainActivity.this, "main", Toast.LENGTH_SHORT).show();
break;
case R.id.secondFragment:
Toast.makeText(MainActivity.this, "second", Toast.LENGTH_SHORT).show();
break;
}
}
});
AppBarConfiguration appBarConfiguration=new AppBarConfiguration.Builder(navController.getGraph()).build();
NavigationUI.setupActionBarWithNavController(this,navController,appBarConfiguration);
}
处理菜单项点击事件:
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
return NavigationUI.onNavDestinationSelected(item,navController)||super.onOptionsItemSelected(item);
}
处理从SecondFragment到MainFragment的返回事件:
@Override
public boolean onSupportNavigateUp() {
return NavigationUI.navigateUp(navController,appBarConfiguration)||super.onSupportNavigateUp();
}
六.深层链接DeepLink
DeepLink的常见应用场景如下:当应用程序收到某个通知推送,你希望用户在单击该通知后,能够跳转到展示该通知内容的页面。接下来,我们使用PendingIntent+DeepLink来实现这个功能,代码如下:
@RequiresApi(api = Build.VERSION_CODES.O)
public void sendNotification(){
NotificationManager notificationManager= (NotificationManager)getActivity().getSystemService(NOTIFICATION_SERVICE);
NotificationChannel channel=new NotificationChannel("MainFragment","跳转",NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(channel);
Notification.Builder builder=new Notification.Builder(getActivity(),"MainFragment");
builder.setAutoCancel(true);
builder.setSmallIcon(R.mipmap.ic_launcher_round);
builder.setContentTitle("设置");
builder.setContentText("点击查看详情");
builder.setContentIntent(getPendingIntent());
Notification notification = builder.build();
notificationManager.notify(1,notification);
}
public PendingIntent getPendingIntent(){
Bundle bundle=new Bundle();
bundle.putString("username","jack");
return Navigation.findNavController(getActivity(),R.id.nav_host_fragment).createDeepLink()
.setGraph(R.navigation.nav_graph)
.setDestination(R.id.secondFragment)
.setArguments(bundle)
.createPendingIntent();
} @RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onStart() {
super.onStart();
sendNotification();
}
当点击通知是,会自动跳转到我们在PendingIntent中设置好的目的地,也就是SecondFragment页面。
Navigation的用法的更多相关文章
- React Navigation基本用法
/** * Created by apple on 2018/9/23. */ import React, { Component } from 'react'; import {AppRegistr ...
- arcgis api for javascript 学习(四) 地图的基本操作
1.文章讲解的为地图的平移.放大.缩小.前视图.后视图以及全景视图的基本功能操作 2.主要用到的是arcgis api for javascript中Navigation的用法,代码如下: <! ...
- Android官方导航栏ActionBar(二)—— Action View、Action Provider、Navigation Tabs的详细用法
在上一篇文章(Android之官方导航栏ActionBar)中,我们介绍了ActionBar各组成部分的基本应用.ActionBar除了提供Action Buttons外,还提供了多种导航方式如 Ac ...
- Navigation Drawer的使用及遇到的问题
ActionBar的问题 Navigation View是Android Support Library中的一个新的组件,该组件提供类似于Sliding Menu的抽屉功能,在张兴业的博客中有讲解到具 ...
- SWIFT UITableView的基本用法
import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: ...
- grep的用法
grep的用法首先创建我们练习grep命令时需要用到的demo文件demo_file. $ cat demo_file THIS LINE IS THE 1ST UPPER CASE LINE IN ...
- query插件之ajaxForm ajaxSubmit的理解用法
如今ajax满天飞,作为重点的form自然也受到照顾. 其实,我们在平常使用Jquery异步提交表单,一般是在submit()中,使用$.ajax进行.比如: $(function(){ $('# ...
- Jquery插件之ajaxForm ajaxSubmit的理解用法
如今ajax满天飞,作为重点的form自然也受到照顾. 其实,我们在平常使用Jquery异步提交表单,一般是在submit()中,使用$.ajax进行.比如: $(function(){ $( ...
- Jquery插件之ajaxForm ajaxSubmit的理解用法(转)
我们在平常使用Jquery异步提交表单,一般是在submit()中,使用$.ajax进行.比如: $(function(){ $('#myForm').submit(function(){ $.aja ...
- react-native导航器 react navigation 介绍
开发环境搭建好之后,想要进一步了解react-native,可以先从react-native官网上的电影列表案例入手: https://reactnative.cn/docs/0.51/sample- ...
随机推荐
- AcWing100 IncDec Sequence
求出\(a\)的差分序列\(b\),其中\(b_1 = a_1, b_2 = a_2 - a_1, ... b_n = a_n - a_{n - 1}\) 根据题意以及公式可以发现,如果我们想让序列所 ...
- 决策树C4.5算法的技术深度剖析、实战解读
在本篇深入探讨的文章中,我们全面分析了C4.5决策树算法,包括其核心原理.实现流程.实战案例,以及与其他流行决策树算法(如ID3.CART和Random Forests)的比较.文章不仅涵盖了丰富的理 ...
- FreeSWITCH在answer前主动发dtmf
操作系统 :CentOS 7.6_x64 FreeSWITCH版本 :1.10.9 NAT环境的主动外呼场景下,会遇到线路侧回铃音数据无法接收的问题,需要FreeSWITCH主动发送RTP数据,发送D ...
- 快速认识,后端王者语言:Java
Java作为最热门的开发语言之一,长居各类排行榜的前三.所以,就算你目前不是用Java开发,你应该了解Java语言的特点,能用来做什么,以备不时之需. Java 是一种高级.多范式编程语言,以其编译为 ...
- [NOI online2022普及C]字符串
题目描述 Kri 非常喜欢字符串,所以他准备找 \(t\) 组字符串研究. 第 \(i\) 次研究中,Kri 准备了两个字符串 \(S\) 和\(R\) ,其中 \(S\) 长度为 \(n\),且只由 ...
- Java并发(十八)----常见线程安全类及实例分析
1.常见线程安全类 String Integer StringBuffer Random Vector Hashtable java.util.concurrent (JUC)包下的类 这里说它们是线 ...
- C++ Qt开发:标准Dialog对话框组件
Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍标准对话框QI ...
- Odoo16—权限控制
odoo的权限控制是通过用户组来实现的,在用户组中配置控制权限,然后再添加用户到用户组中,从而实现对用户的访问和操作权限控制.一个用户可以属于多个用户组,用户最终的权限范围取决于所属用户组权限的并集. ...
- Gateway:Spring Cloud API网关组件
Gateway:Spring Cloud API网关组件 问题总结 API网关? Spring Cloud Gateway? GateWay的工作流程?(重点) Predicate断言? Filter ...
- CMU DLSys 课程笔记 2 - ML Refresher / Softmax Regression
CMU DLSys 课程笔记 2 - ML Refresher / Softmax Regression 本节 Slides | 本节课程视频 这一节课是对机器学习内容的一个复习,以 Softmax ...