Android---Jetpack之Navigation

news/2024/11/30 10:54:47/

目录

为此,Jetpack 提供了 Navigation 组件,旨在方便我们管理页面和 App Bar。 

 Navigation 的优势

添加页面切换动画效果 

普通方式与 safe args  插件方式参数传递

完整 Demo


Navigation 的诞生

Activity 嵌套多个 Fragment 的 UI 架构模式已经非常普遍,但是对 Fragment 的管理一直是一件比较麻烦的事情。我们需要通过 FragmentManager 和 FragmentTransaction 来管理 Fragment 之间的切换。页面的切换通常还包括对应用程序 App bar 的管理、Fragment 间的切换动画,以及 Fragment 间的参数传递。纯代码的方式使用起来不是特别友好,并且 Fragment 和 App bar 在管理和使用的过程中显得混乱。

为此,Jetpack 提供了 Navigation 组件,旨在方便我们管理页面和 App Bar。 

 Navigation 的优势

  \bullet 可视化的页面导航图,类似于 Apple Xcode 中的 StoryBoard,便于我们理清页面关系。

  \bullet 通过 destination 和 action 完成页面间的导航。

  \bullet 方便添加页面切换动画。

  \bullet 页面间类型安全的参数传递。

  \bullet 通过 NavigationUI,对菜单底部导航抽屉菜单导航进行统一的管理。

  \bullet 支持深层链接 DeepLink。

Navigation 的主要元素

  \bullet Navigation Graph,一种新的 xml 资源文件,包含应用程序所有的页面,以及页面间的关系。

  \bullet NavHostFragment,一种特殊的 Fragment,可以将它看作是其他 Fragment 的容器,                    Navigation Grapth 中的 Fragment 正是通过 NavHostFragment 进行展示的。

  \bullet NavController,用于在代码中完成 Navigation Graph 中具体的页面切换工作。

它们三者之间的关系:

当你想切换 Fragment 时,使用 NavController 对象,告诉它你想要去 Navigation Graph 中的哪个 Fragment,NavController 会将你想去的 Fragment 展示在 NavHostFragment 中。

Navigation 应用

当点击fragment_home 里 Button 时,可以由 fragment_home 跳转到 fragment_detail。同理,当点击fragment_detail 里 Button 时,可以由 fragment_detail 跳转到 fragment_home。这个功能由navigation 实现。

步骤1:创建 HomeFragment/DetailFragment,并修改布局

fragment_home.xml / fragment_detail.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"android:layout_height="match_parent"tools:context=".HomeFragment"><Buttonandroid:id="@+id/button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Button"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

步骤2:创建一个 Navigation 目录

在 Navigation 目录创建完毕后,会创建一个 my_nav_graph.xml(名字可以自取) 的文件。

 my_nav_graph.xml 代码

<?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/my_nav_graph"app:startDestination="@id/homeFragment"><fragmentandroid:id="@+id/homeFragment"android:name="com.example.navigation.HomeFragment"android:label="fragment_home"tools:layout="@layout/fragment_home" ><actionandroid:id="@+id/action_homeFragment_to_detailFragment"app:destination="@id/detailFragment" /></fragment><fragmentandroid:id="@+id/detailFragment"android:name="com.example.navigation.DetailFragment"android:label="fragment_detail"tools:layout="@layout/fragment_detail" ><actionandroid:id="@+id/action_detailFragment_to_homeFragment"app:destination="@id/homeFragment" /></fragment>
</navigation>

 注意action属性,即 homeFragment 到 detailFragment 和 detailFragment 到 homeFragment

<actionandroid:id="@+id/action_homeFragment_to_detailFragment"app:destination="@id/detailFragment" />
<actionandroid:id="@+id/action_detailFragment_to_homeFragment"app:destination="@id/homeFragment" />

步骤3: 在 activity_main.xml 里创建NavHostFragment

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><fragmentandroid:id="@+id/fragmentContainerView"android:name="androidx.navigation.fragment.NavHostFragment"android:layout_width="409dp"android:layout_height="729dp"app:defaultNavHost="true"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:navGraph="@navigation/my_nav_graph" /></androidx.constraintlayout.widget.ConstraintLayout>

 步骤4:MainActivity.java。设置 NavController

package com.example.navigation;import androidx.appcompat.app.AppCompatActivity;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.NavigationUI;import android.os.Bundle;public class MainActivity extends AppCompatActivity {private NavController navController;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//TODO NavController 管理 NavHostFragment 里的 Fragment 的切换navController = Navigation.findNavController(this, R.id.fragmentContainerView);NavigationUI.setupActionBarWithNavController(this, navController);}/*** 支持顶部的返回键起作用*/@Overridepublic boolean onSupportNavigateUp() {return navController.navigateUp();}
}

步骤5:HomeFragment.java。点击 Button 实现跳转

package com.example.navigation;import android.os.Bundle;import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;public class HomeFragment extends Fragment {public HomeFragment() {// Required empty public constructor}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {// Inflate the layout for this fragmentreturn inflater.inflate(R.layout.fragment_home, container, false);}/*** 事件监听的设置*/@Overridepublic void onActivityCreated(@Nullable Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);Button btn1 = getView().findViewById(R.id.button);btn1.setOnClickListener(v -> {//TODO 点击按钮后跳转// v 就是我们的 button,它是在HomeFragment上, 我们在 MainActivity 里设置的NavController,所以就能找到NavController navController = Navigation.findNavController(v);// navigate-->导航:从 homeFragment --> detailFragmentnavController.navigate(R.id.action_homeFragment_to_detailFragment);});}
}

步骤6:DetailFragment.java。点击 Button 跳转回来

 package com.example.navigation;import android.os.Bundle;import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.NavigationUI;import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;public class DetailFragment extends Fragment {@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {// Inflate the layout for this fragmentreturn inflater.inflate(R.layout.fragment_detail, container, false);}@Overridepublic void onActivityCreated(@Nullable Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);Button btn1 = getView().findViewById(R.id.button2);btn1.setOnClickListener(v -> {NavController navController = Navigation.findNavController(v);navController.navigate(R.id.action_detailFragment_to_homeFragment);});}
}

添加页面切换动画效果 

添加完后,可以看到 xml 布局里也添加了响应的代码。当然也可以自己设置动画效果 

 

普通方式与 safe args  插件方式参数传递

1. 普通方式,通过 Bundle 实现。

在 HomeFragment 里传递参数到 DetailFragment。

 在 DetailFragment 里接收参数,并打印

 查看结果

 2. safe args  插件方式。普通方式存在一些问题,所以就需要更安全的方式来完成。

首先给整个工程添加插件

根目录的 build.gradle 里添加如下代码

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

 在 app 的 build.gradle 里添加对插件的引用

id 'androidx.navigation.safeargs'

 在 my_nav_graph 里添加要传递的参数。如果是 HomeFragment 传参到 DetailFragment ,那么就在 action 为 "action_homeFragment_to_detailFragment" 里设置参数。同理,如果是由 DetailFragment 传参到 HomeFragment

在 HomeFragment 里通过 HomeFragmentArgs 来完成参数的传递

DetailFragment 里获取参数

 查看打印结果

NavigationUI 作用

Fragment 的切换,除了 Fragment 页面本身的切换,通常还伴有 App bar 的变化。为了方便统一管理,Navigation 组件引入了 NavigationUI 类。

深层链接 DeepLink

方式1:PendingIntent

\bullet 当 App 受到某个通知推送,我们希望用户在点击该通知时,能够直接跳转到展示该通知内容的页面,可以通过 PendingIntent 来完成。

应用实现

布局和前面的案例是一样的,也可以根据后面提供的完整 demo(navigation3 module 里) 进行查看。

HomeFragment.java 里点击按钮---> 发送通知 

 sendNotification() 

private void sendNotification() {// 通知渠道,当是用一个应用的通知,会折叠到一起 (O == 26)if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {// 定义一个 channelNotificationChannel channel = new NotificationChannel(getActivity().getPackageName(),"MyChannel", NotificationManager.IMPORTANCE_DEFAULT);channel.setDescription("My NotificationChannel");//TODO NotificationManager 来创建我们的 NotificationChannelNotificationManager notificationManager = getActivity().getSystemService(NotificationManager.class);notificationManager.createNotificationChannel(channel);}// 创建一个通知Notification notification = null;notification = new NotificationCompat.Builder(getActivity(), getActivity().getPackageName()).setSmallIcon(R.drawable.ic_launcher_background).setContentTitle("Deep Link").setContentText("点击我试试...").setPriority(NotificationCompat.PRIORITY_DEFAULT) // 设置优先级.setContentIntent(getPendingIntent()) //TODO 使用 PendingIntent.build();// 发送通知NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(getActivity());notificationManagerCompat.notify(notificationId++, notification);}

getPendingIntent() 方法会返回一个 PendingIntent 对象

 /*** 返回一个 PendingIntent*/private PendingIntent getPendingIntent() {return Navigation.findNavController(requireActivity(), R.id.button).createDeepLink().setGraph(R.navigation.my_nav_graph).setDestination(R.id.detailFragment).createTaskStackBuilder().getPendingIntent(0, PendingIntent.FLAG_IMMUTABLE);}

 

方式2:URL 方式

\bullet 当用户通过手机浏览器浏览网站上某个页面时,可以在网页上放置一个类似于“在应用内打开”的按钮,如果用户的手机安装有我们的 App,那么通过 DeepLink 就能打开相应的页面;如果没有安装,那么网站可以导航到应用程序的下载页面,引导用户安装应用程序。

完整 Demo

提供上述两个应用的完整代码

链接:https://pan.baidu.com/s/1JLres5nSXqBZkW0IuClbbA 
提取码:4opc


http://www.ppmy.cn/news/41616.html

相关文章

Linux实战案列-发送告警邮件

发送告警邮件 准备 外部邮件服务器 首发在雪月书韵茶香 原因 本地自带邮箱容易被过滤&#xff0c;需要延迟性低的邮箱发送 配置docker 配置环境变量 主机版本&#xff1a;macOS 12.6.4 open .bash_profile export DOCKER_PATH"/Applications/Docker.app/Contents/Re…

OceanMind海睿思受邀参加中小企业数字化转型发展论坛

近日&#xff0c;由江苏省企业信息化协会主办的中小企业数字化转型发展论坛于南京圆满结束。论坛重点邀请了南京市中小企业制造标杆、专精特新“小巨人”企业等相关信息化负责人和IT工作者共同探讨中小企业数字化转型的发展路径。 OceanMind海睿思作为南京地区大数据领域优秀代…

ActiveMQ使用(三):在JavaScript中使用mqttws31.js

ActiveMQ使用(三):在JavaScript中使用mqttws31.js 1. 环境准备 jQuery-1.10 下载地址:https://www.jsdelivr.com/package/npm/jquery-1.10.2?tabfilesmqttws31.js: 下载地址:https://www.jsdelivr.com/package/npm/ng2-mqtt 2. 相关代码 <!DOCTYPE html> <html …

Python 异常处理之try except else

try ... except 语句具有可选的 else 子句&#xff0c;该子句如果存在&#xff0c;它必须放在所有 except 子句 之后。 它适用于 try 子句 没有引发异常但又必须要执行的代码。 例如: for arg in sys.argv[1:]:try:f open(arg, r)except OSError:print(cannot open, arg)else…

【深一点学习】我用CPU也能跟着沐神实现单发多框检测(SSD),从底层了解目标检测任务的实现过程,需要什么样的方法调用。《动手学深度学习》Yes,沐神,Yes

目标检测近年来已经取得了很重要的进展&#xff0c;主流的算法主要分为两个类型[1611.06612] RefineNet: Multi-Path Refinement Networks for High-Resolution Semantic Segmentation (arxiv.org)&#xff1a;&#xff08;1&#xff09;two-stage方法&#xff0c;如R-CNN系算法…

【JavaWeb】Tomcat的下载及使用

文章目录1.前言2.Tomcat 的下载3.启动Tomcat4.修改Tomcat 的默认端口号5.关闭Tomcat6.部署项目1.前言 Tomcat是Apache软件基金会的一个核心项目, 也是一个开源免费的一个轻量级web服务器.支持servlet/JSP 少量的JavaEE规范,也是我们学习JavaWeb中常用的服务器. Tomcat 也被称为…

react-6 路由 - ts爆红解决

a) 安装路由库 5 版本的 //下载router npm i react-router-dom5//下载去除 路径报错的插件 npm i --save-dev types/react-router-dom 开启配置 允许导入&#xff1a;tsx&#xff1a;因为项目是基于TS的 b) 相关组件 路由管理组件 BrowserRouter 使用 HTML5 历史记录 API…

NumPy 初学者指南中文第三版:11~14

原文&#xff1a;NumPy: Beginner’s Guide - Third Edition 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 十一、玩转 Pygame 本章适用于希望使用 NumPy 和 Pygame 快速轻松创建游戏的开发人员。 基本的游戏开发经验会有所帮助&#xff0c;但这不是必需的。 您将学…