【Jetpack】Navigation 导航组件 ④ ( Fragment 跳转中使用 safe args 安全传递参数 )
一、页面跳转间的传统的数据传递方式1、传统的数据传递方式 - Bundle 传递数据1、Navigation 组件中的 Bundle 数据传递2、传统数据传递实现步骤3、FragmentA 完整代码示例4、FragmentB 完整代码示例5、执行结果2、使用 Bundle 传递数据安全性差二、页面跳转间的传统的数据传递方式1、导入插件依赖2、使用插件3、在 navigation_graph.xml
代码地址 :
- CSDN ( 本博客代码快照 | 推荐下载 0 积分 ) : https://download.csdn.net/download/han1202012/88251933
- GitHub ( 可能已经覆盖 ) : https://github.com/han1202012/Navigation
一、页面跳转间的传统的数据传递方式
1、传统的数据传递方式 - Bundle 传递数据
1、Navigation 组件中的 Bundle 数据传递
之前的 默认 Navigation 跳转方法 , 只需要传入 navigation 资源 ID , 即可完成页面跳转 ;
public open fun navigate(@IdRes resId: Int)
Navigation 机制中 , 还提供了可以传入 Bundle 参数的跳转方法 , 调用该方法 , 可以在页面跳转时 , 传递一个 Bundle 参数 , 其中可以封装一系列的参数键值对 ;
public open fun navigate(@IdRes resId: Int, args: Bundle?)
2、传统数据传递实现步骤
首先 , 创建 Bundle 实例对象 , 向其中封装 “NAME” = “Tom” , “AGE” = 18 , 两组数据 ;
// 定义 Kotlin 常量
private const val ARG_PARAM_NAME = "NAME"
private const val ARG_PARAM_AGE = "AGE"
// 正常方式传递参数
var args: Bundle = Bundle().apply {
// 设置 Bundle 对象参数数据
this.putString(ARG_PARAM_NAME, "Tom")
this.putInt(ARG_PARAM_AGE, 18)
}
然后 , 调用 Navigation#findNavController 函数 , 获取 NavigationController ;
// 获取 NavigationController
val navController = Navigation.findNavController(it)
// 按照 action_fragmentA_to_fragmentB 对应的 action 的导航路线走
navController.navigate(R.id.action_fragmentA_to_fragmentB, args)
再后 , 调用 NavigationController#navigate 方法 , 传入对应的 Navigation 导航资源 和 要传递的 Bundle 参数 ;
// 按照 action_fragmentA_to_fragmentB 对应的 action 的导航路线走
navController.navigate(R.id.action_fragmentA_to_fragmentB, args)
最后 , 在跳转后的界面中 , 调用 getArguments 函数 , 并获取 NAME 和 AGE 对应的参数值 ;
// 定义 Kotlin 常量
private const val ARG_PARAM_NAME = "NAME"
private const val ARG_PARAM_AGE = "AGE"
arguments?.let {
name = it.getString(ARG_PARAM_NAME)
age = it.getInt(ARG_PARAM_AGE)
}
3、FragmentA 完整代码示例
FragmentA 完整代码示例 :
package kim.hsl.nav
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.navigation.Navigation
// 定义 Kotlin 常量
private const val ARG_PARAM_NAME = "NAME"
private const val ARG_PARAM_AGE = "AGE"
class FragmentB : Fragment() {
private var name: String? = null
private var age: Int? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
name = it.getString(ARG_PARAM_NAME)
age = it.getInt(ARG_PARAM_AGE)
}
Log.i("TAG", "FragmentA 传递到 FragmentB 的参数为 name = $name , age = $age")
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// 设置 Fragment 布局文件
return inflater.inflate(R.layout.fragment_b, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val button = view.findViewById<Button>(R.id.button)
button.setOnClickListener {
// 获取 NavigationController
val navController = Navigation.findNavController(it)
// 按照 action_fragmentB_to_fragmentA 对应的 action 的导航路线走
navController.navigate(R.id.action_fragmentB_to_fragmentA)
}
}
}
4、FragmentB 完整代码示例
FragmentB 完整代码示例 :
package kim.hsl.nav
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.navigation.Navigation
// 定义 Kotlin 常量
private const val ARG_PARAM_NAME = "NAME"
private const val ARG_PARAM_AGE = "AGE"
class FragmentB : Fragment() {
private var name: String? = null
private var age: Int? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
name = it.getString(ARG_PARAM_NAME)
age = it.getInt(ARG_PARAM_AGE)
}
Log.i("TAG", "FragmentA 传递到 FragmentB 的参数为 name = $name , age = $age")
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// 设置 Fragment 布局文件
return inflater.inflate(R.layout.fragment_b, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val button = view.findViewById<Button>(R.id.button)
button.setOnClickListener {
// 获取 NavigationController
val navController = Navigation.findNavController(it)
// 按照 action_fragmentB_to_fragmentA 对应的 action 的导航路线走
navController.navigate(R.id.action_fragmentB_to_fragmentA)
}
}
}
5、执行结果
运行应用 , 进入界面后 , 自动进入 默认的 FragmentA 界面 ,
点击 " 跳转到 B " , 此时 , 跳转到 FragmentB 界面 :
此时 Logcat 日志面板 , 输出如下内容 :
kim.hsl.nav I/TAG: FragmentA 传递到 FragmentB 的参数为 name = Tom , age = 18
2、使用 Bundle 传递数据安全性差
使用 传统的方式 , 在 Fragment 之间 传递 数据 , 类型很不安全 ,
设置 传递的数据时 , 需要设置 放入的 数据类型 , 如下代码所示 :
// 正常方式传递参数
var args: Bundle = Bundle().apply {
// 设置 Bundle 对象参数数据
this.putString("NAME", "Tom")
this.putInt("AGE", 18)
}
上面的代码中 , 向 Bundle 中设置了如下两个数据 :
- 设置了 String 类型的数据 , 名称是 “NAME” 字符串常量 , 值为 字符串 “Tom” ,
- 设置了 Int 类型的数据 , 名称是 “AGE” 字符串常量 , 值为 整型 18 ;
这里要注意 , 设置的时候 , 设置的 NAME 属性值是 String 类型的 , 那么在 FragmentB 中获取的 NAME 属性值也必须是 String 类型的 ,
arguments?.let {
name = it.getString("NAME")
}
此处 没有 类型检查 , 即使你写错了具体的 属性值 名称 和 属性值 类型 , 编译器也不会报错 , 但是在执行时 , 会出现错误 ;
下面的代码中 , 调用 getInt(“Name”) 也不会报错 ;
上面的 使用 Bundle 在 Fragment 之间传递 参数 , 没有类型检查 , 即使写错了数据类型 也不会报错 , 这就导致了 数据传递 不安全 的问题 , 如果出现问题 , 导致错误很难排查 ;
二、页面跳转间的传统的数据传递方式
1、导入插件依赖
安全参数传递需要使用到 androidx.navigation:navigation-safe-args-gradle-plugin:2.3.0-alpha06
中的 androidx.navigation.safeargs
插件 ;
由于在最新版的 Gradle 配置中 , 使用 根目录下 build.gradle 构建脚本中的 直接配置 plugins 插件的方式 , 无法获取到该 androidx.navigation.safeargs
插件 , 因此放弃该方案 , 将 该脚本的 整个 plugins 代码块完全注释掉 ;
plugins {
id 'com.android.application' version '7.3.1' apply false
id 'com.android.library' version '7.3.1' apply false
id 'org.jetbrains.kotlin.android' version '1.7.20' apply false
id 'androidx.navigation.safeargs' version '2.3.0-alpha06' apply false
}
在 settings.gradle 中 , 使用传统的方式配置 Gralde 编译过程中使用到的插件 ;
下面的章节中 , 可以查看该 settings.gradle 配置的完整源码 ;
配置如下 :
buildscript {
repositories {
google()
mavenCentral()
jcenter()
maven {
url 'https://maven.aliyun.com/repository/public/'
}
maven{
url 'https://maven.aliyun.com/repository/google/'
}
}
dependencies {
classpath "com.android.tools.build:gradle:7.3.1"
classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.3.0-alpha06'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
2、使用插件
在 Module 下的 build.gradle 中 , 使用 androidx.navigation.safeargs 依赖 ;
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'androidx.navigation.safeargs'
}
3、在 navigation_graph.xml 中定义要传递的 argument 参数信息
如果要从 FragmentB 跳转到 FragmentA 页面时 , 传递数据 , 就将参数信息设置在该 FragmentB 对应的配置文件中 ;
参数格式为 :
<argument
android:name="NAME"
app:argType="string"
android:defaultValue="Jerry"/>
- 参数名称为 " NAME " ;
- 参数类型是 string 类型 ;
- 参数默认值是 “Jerry” ;
完整的参数配置如下 :
<fragment
android:id="@+id/fragmentB"
android:name="kim.hsl.nav.FragmentB"
android:label="fragment_b"
tools:layout="@layout/fragment_b" >
<action
android:id="@+id/action_fragmentB_to_fragmentA"
app:destination="@id/fragmentA"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim" />
<!-- 配置完毕后 菜单栏/Build/Make 编译一下,
自动生成 FragmentBArgs.java 代码, 之后调用该自动生成的类传参 -->
<argument
android:name="NAME"
app:argType="string"
android:defaultValue="Jerry"/>
<argument
android:name="AGE"
app:argType="integer"
android:defaultValue="12"/>
</fragment>
4、重新编译生成参数传递相关代码
FragmentB 中 配置完毕 参数相关配置 后 , 选择 " 菜单栏 / Build / Make " 选项 , 重新编译一下,
目的是为了 生成 FragmentBArgs.java 代码, 之后调用该自动生成的类 进行 传参 ;
生成的类在 " Navigation\app\build\generated\source\navigation-args\debug\kim\hsl\nav " 目录下 ,
生成的 FragmentBArgs.java 代码如下 : ( 仅做参考 )
package kim.hsl.nav;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.navigation.NavArgs;
import java.lang.IllegalArgumentException;
import java.lang.Object;
import java.lang.Override;
import java.lang.String;
import java.lang.SuppressWarnings;
import java.util.HashMap;
public class FragmentBArgs implements NavArgs {
private final HashMap arguments = new HashMap();
private FragmentBArgs() {
}
private FragmentBArgs(HashMap argumentsMap) {
this.arguments.putAll(argumentsMap);
}
@NonNull
@SuppressWarnings("unchecked")
public static FragmentBArgs fromBundle(@NonNull Bundle bundle) {
FragmentBArgs __result = new FragmentBArgs();
bundle.setClassLoader(FragmentBArgs.class.getClassLoader());
if (bundle.containsKey("NAME")) {
String NAME;
NAME = bundle.getString("NAME");
if (NAME == null) {
throw new IllegalArgumentException("Argument \"NAME\" is marked as non-null but was passed a null value.");
}
__result.arguments.put("NAME", NAME);
} else {
__result.arguments.put("NAME", "Jerry");
}
if (bundle.containsKey("AGE")) {
int AGE;
AGE = bundle.getInt("AGE");
__result.arguments.put("AGE", AGE);
} else {
__result.arguments.put("AGE", 12);
}
return __result;
}
@SuppressWarnings("unchecked")
@NonNull
public String getNAME() {
return (String) arguments.get("NAME");
}
@SuppressWarnings("unchecked")
public int getAGE() {
return (int) arguments.get("AGE");
}
@SuppressWarnings("unchecked")
@NonNull
public Bundle toBundle() {
Bundle __result = new Bundle();
if (arguments.containsKey("NAME")) {
String NAME = (String) arguments.get("NAME");
__result.putString("NAME", NAME);
} else {
__result.putString("NAME", "Jerry");
}
if (arguments.containsKey("AGE")) {
int AGE = (int) arguments.get("AGE");
__result.putInt("AGE", AGE);
} else {
__result.putInt("AGE", 12);
}
return __result;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object == null || getClass() != object.getClass()) {
return false;
}
FragmentBArgs that = (FragmentBArgs) object;
if (arguments.containsKey("NAME") != that.arguments.containsKey("NAME")) {
return false;
}
if (getNAME() != null ? !getNAME().equals(that.getNAME()) : that.getNAME() != null) {
return false;
}
if (arguments.containsKey("AGE") != that.arguments.containsKey("AGE")) {
return false;
}
if (getAGE() != that.getAGE()) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = 1;
result = 31 * result + (getNAME() != null ? getNAME().hashCode() : 0);
result = 31 * result + getAGE();
return result;
}
@Override
public String toString() {
return "FragmentBArgs{"
+ "NAME=" + getNAME()
+ ", AGE=" + getAGE()
+ "}";
}
public static class Builder {
private final HashMap arguments = new HashMap();
public Builder(FragmentBArgs original) {
this.arguments.putAll(original.arguments);
}
public Builder() {
}
@NonNull
public FragmentBArgs build() {
FragmentBArgs result = new FragmentBArgs(arguments);
return result;
}
@NonNull
public Builder setNAME(@NonNull String NAME) {
if (NAME == null) {
throw new IllegalArgumentException("Argument \"NAME\" is marked as non-null but was passed a null value.");
}
this.arguments.put("NAME", NAME);
return this;
}
@NonNull
public Builder setAGE(int AGE) {
this.arguments.put("AGE", AGE);
return this;
}
@SuppressWarnings("unchecked")
@NonNull
public String getNAME() {
return (String) arguments.get("NAME");
}
@SuppressWarnings("unchecked")
public int getAGE() {
return (int) arguments.get("AGE");
}
}
}
5、调用 FragmentBArgs 类生成参数 Bundle
在 FragmentB 中 ,
首先 , 调用 FragmentBArgs#Builder() , 创建 参数创建者类 ,
然后 , 调用 setNAME 和 setAGE 分别设置 参数 ,
再后 , 调用 FragmentBArgs.Builder#build() 函数 , 创建 FragmentBArgs 类型的 参数对象 ,
最后 , 调用 FragmentBArgs#toBundle() 函数 , 将 FragmentBArgs 对象转为 Bundle 类型对象 ;
var args: Bundle = FragmentBArgs.Builder()
.setNAME("Trump")
.setAGE(80)
.build().toBundle()
创建完 Bundle 对象之后 , 将其传给 NavigationController#navigate 函数 , 进行页面跳转 ;
var args: Bundle = FragmentBArgs.Builder()
.setNAME("Trump")
.setAGE(80)
.build().toBundle()
// 获取 NavigationController
val navController = Navigation.findNavController(it)
// 按照 action_fragmentB_to_fragmentA 对应的 action 的导航路线走
navController.navigate(R.id.action_fragmentB_to_fragmentA, args)
后续章节可以查看 FragmentB 的完整代码 ;
6、FragmentA 中获取参数
在 FragmentA 中 , 调用 getArguments 函数 , 获取页面跳转传递的 Bundle 对象即可 ;
arguments?.let {
name = it.getString(ARG_PARAM_NAME)
age = it.getInt(ARG_PARAM_AGE)
}
Log.i("TAG", "FragmentB 传递到 FragmentA 的参数为 name = $name , age = $age")
三、两种传参方式的完整代码示例
1、Gradle 构建脚本
I、根目录下 settings.gradle 构建脚本
该构建脚本中 , pluginManagement 是最新的 Gradle 配置 , 但是本项目中没有启用 , 注释掉也可以运行 ;
buildscript 是老版本的 Gradle 编译时依赖配置 , 由于本次使用了 androidx.navigation.safeargs 插件 , 该依赖使用新方式配置无法成功下载 , 这里直接使用老的配置方式 ;
dependencyResolutionManagement 中配置的是依赖库的下载地址 ;
settings.gradle 构建脚本代码示例 :
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
jcenter()
maven {
url 'https://maven.aliyun.com/repository/public/'
}
maven{
url 'https://maven.aliyun.com/repository/google/'
}
}
}
buildscript {
repositories {
google()
mavenCentral()
jcenter()
maven {
url 'https://maven.aliyun.com/repository/public/'
}
maven{
url 'https://maven.aliyun.com/repository/google/'
}
}
dependencies {
classpath "com.android.tools.build:gradle:7.3.1"
classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.3.0-alpha06'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "Navigation"
include ':app'
II、根目录下 build.gradle 构建脚本
这是新的 Gradle 语法配置 , 需要结合 pluginManagement 配置使用 , 由于下面的配置无法成功下载 androidx.navigation.safeargs 依赖 , 整体作废 ;
根目录下 build.gradle 构建脚本 :
// Top-level build file where you can add configuration options common to all sub-projects/modules.
/*plugins {
id 'com.android.application' version '7.3.1' apply false
id 'com.android.library' version '7.3.1' apply false
id 'org.jetbrains.kotlin.android' version '1.7.20' apply false
id 'androidx.navigation.safeargs' version '2.3.0-alpha06' apply false
}*/
III、Module 目录下 build.gradle 构建脚本
该配置没有需要注意的 , 导入 androidx.navigation.safeargs 插件就行 ;
Module 目录下 build.gradle 构建脚本 :
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'androidx.navigation.safeargs'
}
android {
namespace 'kim.hsl.nav'
compileSdk 32
defaultConfig {
applicationId "kim.hsl.nav"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'androidx.navigation:navigation-fragment-ktx:2.4.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.4.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
2、res 资源配置
Resources 资源配置 , 主要是配置 Navigation 相关的 NavigationGraph ;
I、MainActivity 页面布局
这是 主页面 Launcher Activity 的布局 , 之后的 Fragment 的 布局 就替换到 fragment 标签位置 ;
MainActivity 页面布局 :
<?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">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<fragment
android:id="@+id/fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/navigation_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
II、FragmentA 页面布局
页面布局就是一个简单的 FrameLayout 布局 , 要先创建 Fragment 布局 , 然后才能在 navigation_graph.xml 配置该布局 ;
FragmentA 页面布局 :
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FragmentA">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="跳转到 B"
android:onClick="onClick" />
</FrameLayout>
III、FragmentB 页面布局
页面布局就是一个简单的 FrameLayout 布局 , 要先创建 Fragment 布局 , 然后才能在 navigation_graph.xml 配置该布局 ;
FragmentB 页面布局 :
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FragmentB">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="跳转到 A"
android:onClick="onClick" />
</FrameLayout>
IV、navigation_graph.xml 配置
在 res 目录下 , 创建 navigation 目录 , 然后在该目录中创建 navigation_graph.xml 配置文件 , 用于配置 页面跳转 相关参数 ;
具体的参数含义 , 可以参考之前的博客 ;
navigation_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/navigation_graph"
app:startDestination="@id/fragmentA">
<fragment
android:id="@+id/fragmentA"
android:name="kim.hsl.nav.FragmentA"
android:label="fragment_a"
tools:layout="@layout/fragment_a" >
<action
android:id="@+id/action_fragmentA_to_fragmentB"
app:destination="@id/fragmentB"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim" />
</fragment>
<fragment
android:id="@+id/fragmentB"
android:name="kim.hsl.nav.FragmentB"
android:label="fragment_b"
tools:layout="@layout/fragment_b" >
<action
android:id="@+id/action_fragmentB_to_fragmentA"
app:destination="@id/fragmentA"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim" />
<!-- 配置完毕后 菜单栏/Build/Make 编译一下,
自动生成 FragmentBArgs.java 代码, 之后调用该自动生成的类传参 -->
<argument
android:name="NAME"
app:argType="string"
android:defaultValue="Jerry"/>
<argument
android:name="AGE"
app:argType="integer"
android:defaultValue="12"/>
</fragment>
</navigation>
3、页面相关 Kotlin 代码
主要是 Activity 和 Fragment 代码 ;
I、MainActivity 页面代码
这是主页面 , 复杂使用 Navigation 添加 Fragment ;
package kim.hsl.nav
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.Navigation.findNavController
import androidx.navigation.ui.NavigationUI
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// fragmentContainerView 组件的 管理 操作通过 NavController 完成
// 对应的就是 navController 实例变量
val navController = findNavController(this, R.id.fragment)
NavigationUI.setupActionBarWithNavController(this, navController)
}
}
II、FragmentA 页面代码
FragmentA 跳转到 FragmentB 使用传统的方式传递参数 , 类型不安全 ;
FragmentA 页面代码 :
package kim.hsl.nav
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.fragment.app.Fragment
import androidx.navigation.Navigation
// 定义 Kotlin 常量
private const val ARG_PARAM_NAME = "NAME"
private const val ARG_PARAM_AGE = "AGE"
class FragmentA : Fragment() {
private var name: String? = null
private var age: Int? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
name = it.getString(ARG_PARAM_NAME)
age = it.getInt(ARG_PARAM_AGE)
}
Log.i("TAG", "FragmentB 传递到 FragmentA 的参数为 name = $name , age = $age")
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// 设置 Fragment 布局文件
return inflater.inflate(R.layout.fragment_a, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val button = view.findViewById<Button>(R.id.button)
button.setOnClickListener {
// 正常方式传递参数
var args: Bundle = Bundle().apply {
// 设置 Bundle 对象参数数据
this.putString(ARG_PARAM_NAME, "Tom")
this.putInt(ARG_PARAM_AGE, 18)
}
// 获取 NavigationController
val navController = Navigation.findNavController(it)
// 按照 action_fragmentA_to_fragmentB 对应的 action 的导航路线走
navController.navigate(R.id.action_fragmentA_to_fragmentB, args)
}
}
}
III、FragmentB 页面代码
FragmentB 跳转到 FragmentA 使用安全方式传递参数 ;
FragmentB 页面代码 :
package kim.hsl.nav
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.navigation.Navigation
// 定义 Kotlin 常量
private const val ARG_PARAM_NAME = "NAME"
private const val ARG_PARAM_AGE = "AGE"
class FragmentB : Fragment() {
private var name: String? = null
private var age: Int? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
name = it.getString(ARG_PARAM_NAME)
age = it.getInt(ARG_PARAM_AGE)
}
Log.i("TAG", "FragmentA 传递到 FragmentB 的参数为 name = $name , age = $age")
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// 设置 Fragment 布局文件
return inflater.inflate(R.layout.fragment_b, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val button = view.findViewById<Button>(R.id.button)
button.setOnClickListener {
var args: Bundle = FragmentBArgs.Builder()
.setNAME("Trump")
.setAGE(80)
.build().toBundle()
// 获取 NavigationController
val navController = Navigation.findNavController(it)
// 按照 action_fragmentB_to_fragmentA 对应的 action 的导航路线走
navController.navigate(R.id.action_fragmentB_to_fragmentA, args)
}
}
}
4、执行结果
编译运行程序 , 进入默认 Launcher 界面 , 默认显示 FragmentA 页面 ,
点击 " 跳转到 B " 按钮 , 此时跳转到了 FragmentB , 使用传统方式传递的参数也能正常获取 ,
kim.hsl.nav I/TAG: FragmentA 传递到 FragmentB 的参数为 name = Tom , age = 18
在 FragmentB 页面点击 " 跳转到 A " 按钮 , 使用安全方式传递的参数 , 也能正常打印出来 ;
代码地址 :
- CSDN ( 本博客代码快照 | 推荐下载 0 积分 ) : https://download.csdn.net/download/han1202012/88251933
- GitHub ( 可能已经覆盖 ) : https://github.com/han1202012/Navigation
更多推荐
所有评论(0)