一.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的用法的更多相关文章

  1. React Navigation基本用法

    /** * Created by apple on 2018/9/23. */ import React, { Component } from 'react'; import {AppRegistr ...

  2. arcgis api for javascript 学习(四) 地图的基本操作

    1.文章讲解的为地图的平移.放大.缩小.前视图.后视图以及全景视图的基本功能操作 2.主要用到的是arcgis api for javascript中Navigation的用法,代码如下: <! ...

  3. Android官方导航栏ActionBar(二)—— Action View、Action Provider、Navigation Tabs的详细用法

    在上一篇文章(Android之官方导航栏ActionBar)中,我们介绍了ActionBar各组成部分的基本应用.ActionBar除了提供Action Buttons外,还提供了多种导航方式如 Ac ...

  4. Navigation Drawer的使用及遇到的问题

    ActionBar的问题 Navigation View是Android Support Library中的一个新的组件,该组件提供类似于Sliding Menu的抽屉功能,在张兴业的博客中有讲解到具 ...

  5. SWIFT UITableView的基本用法

    import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: ...

  6. grep的用法

    grep的用法首先创建我们练习grep命令时需要用到的demo文件demo_file. $ cat demo_file THIS LINE IS THE 1ST UPPER CASE LINE IN ...

  7. query插件之ajaxForm ajaxSubmit的理解用法

    如今ajax满天飞,作为重点的form自然也受到照顾. 其实,我们在平常使用Jquery异步提交表单,一般是在submit()中,使用$.ajax进行.比如:   $(function(){ $('# ...

  8. Jquery插件之ajaxForm ajaxSubmit的理解用法

      如今ajax满天飞,作为重点的form自然也受到照顾. 其实,我们在平常使用Jquery异步提交表单,一般是在submit()中,使用$.ajax进行.比如:   $(function(){ $( ...

  9. Jquery插件之ajaxForm ajaxSubmit的理解用法(转)

    我们在平常使用Jquery异步提交表单,一般是在submit()中,使用$.ajax进行.比如: $(function(){ $('#myForm').submit(function(){ $.aja ...

  10. react-native导航器 react navigation 介绍

    开发环境搭建好之后,想要进一步了解react-native,可以先从react-native官网上的电影列表案例入手: https://reactnative.cn/docs/0.51/sample- ...

随机推荐

  1. 一、Linux发展史

    一.Linux发展史及红帽认证 红帽授权培训合作伙伴 木兰宽松许可证 1. Linux系统发展史 1. Unix发展历程 上世纪六十年代贝尔实验室(Bell).麻省理工学院(MIT)以及通用电气(GE ...

  2. Modbus 转PROFIBUS DP网关在工厂自动温度控制系统中的应用案例

    Modbus 转PROFIBUS DP 网关PM-160 在工厂自动温度控制系统中的应用案例 摘要 随着科技的发展和工业生产水平的提高,自动温度控制系统在纺织.化工.机械等各类工业控制过程中得到了广泛 ...

  3. 一文秒懂|Linux字符设备驱动

    1.前言 众所周知,Linux内核主要包括三种驱动模型,字符设备驱动,块设备驱动以及网络设备驱动. 其中,Linux字符设备驱动,可以说是Linux驱动开发中最常见的一种驱动模型. 我们该系列文章,主 ...

  4. 洛谷4159 [SCOI2009] 迷路(矩阵快速幂,拆点)

    题意:该有向图有 n 个节点,节点从 1至 n 编号,windy 从节点 1 出发,他必须恰好在 t 时刻到达节点 n.现在给出该有向图,你能告诉 windy 总共有多少种不同的路径吗?答案对2009 ...

  5. JSX 代码是如何“摇身一变”成为 DOM 的?

    JSX 是一种语法,并不是 React 中的内容,时下接入 JSX 语法的框架越来越多,但与之缘分最深的仍然是 React.本节来讲一下 React 是如何摇身一变成为 DOM 的. 我们平时在写Re ...

  6. K8s容器debug高级技巧

    使用 kubectl exec 执行指令 如果您在 Kubernetes 上运行软件,您会想要在某些时候去调试您所部署的软件的一些方面.对于习惯于使用虚拟机 (VMs) 的人来说能自然使用的一种简单的 ...

  7. Tomact从认识到安装与详细使用

    一.什么是Tomact? Tomcat是一个开源免费的轻量级Web服务器,它是一个软件程序,主要功能是提供网上信息浏览服务,对HTTP协议的操作进行封装,使得程序员不必对协议进行操作,让Web开发更加 ...

  8. Educational Codeforces Round 26 Problem C

    C. Two Seals time limit per test 1 second memory limit per test 256 megabytes input standard input o ...

  9. 使用dtd定义元素

  10. 2024-01-03:用go语言,给你两个长度为 n 下标从 0 开始的整数数组 cost 和 time, 分别表示给 n 堵不同的墙刷油漆需要的开销和时间。你有两名油漆匠, 一位需要 付费 的油漆匠

    2024-01-03:用go语言,给你两个长度为 n 下标从 0 开始的整数数组 cost 和 time, 分别表示给 n 堵不同的墙刷油漆需要的开销和时间.你有两名油漆匠, 一位需要 付费 的油漆匠 ...