【Jetpack】Navigation 导航组件 ④ ( Fragment 跳转中使用 safe args 安全传递参数 )

文章目录

  • 一、页面跳转间的传统的数据传递方式
    • 1、传统的数据传递方式 - Bundle 传递数据
      • 1、Navigation 组件中的 Bundle 数据传递
      • 2、传统数据传递实现步骤
      • 3、FragmentA 完整代码示例
      • 4、FragmentB 完整代码示例
      • 5、执行结果
    • 2、使用 Bundle 传递数据安全性差
  • 二、页面跳转间的传统的数据传递方式
    • 1、导入插件依赖
    • 2、使用插件
    • 3、在 navigation_graph.xml 中定义要传递的 argument 参数信息
    • 4、重新编译生成参数传递相关代码
    • 5、调用 FragmentBArgs 类生成参数 Bundle
    • 6、FragmentA 中获取参数
  • 三、两种传参方式的完整代码示例
    • 1、Gradle 构建脚本
      • I、根目录下 settings.gradle 构建脚本
      • II、根目录下 build.gradle 构建脚本
      • III、Module 目录下 build.gradle 构建脚本
    • 2、res 资源配置
      • I、MainActivity 页面布局
      • II、FragmentA 页面布局
      • III、FragmentB 页面布局
      • IV、navigation_graph.xml 配置
    • 3、页面相关 Kotlin 代码
      • I、MainActivity 页面代码
      • II、FragmentA 页面代码
      • III、FragmentB 页面代码
    • 4、执行结果


代码地址 :

  • 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.navimport 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? = nullprivate var age: Int? = nulloverride 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 {// 获取 NavigationControllerval navController = Navigation.findNavController(it)// 按照 action_fragmentB_to_fragmentA 对应的 action 的导航路线走navController.navigate(R.id.action_fragmentB_to_fragmentA)}}
}

4、FragmentB 完整代码示例


FragmentB 完整代码示例 :

package kim.hsl.navimport 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? = nullprivate var age: Int? = nulloverride 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 {// 获取 NavigationControllerval 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 falseid 'com.android.library' version '7.3.1' apply falseid 'org.jetbrains.kotlin.android' version '1.7.20' apply falseid '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 对应的配置文件中 ;


参数格式为 :

        <argumentandroid:name="NAME"app:argType="string"android:defaultValue="Jerry"/>
  • 参数名称为 " NAME " ;
  • 参数类型是 string 类型 ;
  • 参数默认值是 “Jerry” ;

完整的参数配置如下 :

    <fragmentandroid:id="@+id/fragmentB"android:name="kim.hsl.nav.FragmentB"android:label="fragment_b"tools:layout="@layout/fragment_b" ><actionandroid: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 代码, 之后调用该自动生成的类传参 --><argumentandroid:name="NAME"app:argType="string"android:defaultValue="Jerry"/><argumentandroid: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")@NonNullpublic String getNAME() {return (String) arguments.get("NAME");}@SuppressWarnings("unchecked")public int getAGE() {return (int) arguments.get("AGE");}@SuppressWarnings("unchecked")@NonNullpublic 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;}@Overridepublic 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;}@Overridepublic int hashCode() {int result = 1;result = 31 * result + (getNAME() != null ? getNAME().hashCode() : 0);result = 31 * result + getAGE();return result;}@Overridepublic 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() {}@NonNullpublic FragmentBArgs build() {FragmentBArgs result = new FragmentBArgs(arguments);return result;}@NonNullpublic 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;}@NonNullpublic Builder setAGE(int AGE) {this.arguments.put("AGE", AGE);return this;}@SuppressWarnings("unchecked")@NonNullpublic 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 falseid 'com.android.library' version '7.3.1' apply falseid 'org.jetbrains.kotlin.android' version '1.7.20' apply falseid '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 32defaultConfig {applicationId "kim.hsl.nav"minSdk 21targetSdk 32versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility 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"><TextViewandroid: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" /><fragmentandroid: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 --><TextViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:text="@string/hello_blank_fragment" /><Buttonandroid: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 --><TextViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:text="@string/hello_blank_fragment" /><Buttonandroid: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"><fragmentandroid:id="@+id/fragmentA"android:name="kim.hsl.nav.FragmentA"android:label="fragment_a"tools:layout="@layout/fragment_a" ><actionandroid: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><fragmentandroid:id="@+id/fragmentB"android:name="kim.hsl.nav.FragmentB"android:label="fragment_b"tools:layout="@layout/fragment_b" ><actionandroid: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 代码, 之后调用该自动生成的类传参 --><argumentandroid:name="NAME"app:argType="string"android:defaultValue="Jerry"/><argumentandroid:name="AGE"app:argType="integer"android:defaultValue="12"/></fragment>
</navigation>

3、页面相关 Kotlin 代码


主要是 Activity 和 Fragment 代码 ;


I、MainActivity 页面代码


这是主页面 , 复杂使用 Navigation 添加 Fragment ;

package kim.hsl.navimport android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.Navigation.findNavController
import androidx.navigation.ui.NavigationUIclass 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.navimport 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? = nullprivate var age: Int? = nulloverride 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)}// 获取 NavigationControllerval 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.navimport 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? = nullprivate var age: Int? = nulloverride 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()// 获取 NavigationControllerval 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/52275.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TCP的可靠性之道:确认重传和流量控制

TCP 全称为 Transmission Control Protocol&#xff08;传输控制协议&#xff09;&#xff0c;是一种面向连接的、可靠的、基于字节流的传输层通信协议&#xff0c;其中可靠性是相对于其他传输协议的优势点。TCP 为了确保数据传输的可靠性主要做了以下几点&#xff1a; 发送确…

电脑文件删除了可以找回吗?分享一种简单恢复删除电脑文件办法!

电脑文件删除了可以找回吗&#xff1f;可以。在原理上讲电脑删除的文件是有希望恢复的&#xff0c;因为操作系统在删除文件的时候并会不会立刻将文件彻底删除。当文件被删除的时候&#xff0c;其文件记录被删除&#xff0c;并且被文件占用的磁盘空间被标记为空闲。 这样对于用户…

1.1 VMware Workstation与Kali的安装和配置1

资源见专栏第一篇文章https://blog.csdn.net/algorithmyyds/article/details/132457258 安装VMware 不多加赘述&#xff0c;直接按顺序安装即可。 有以下需注意的地方&#xff1a; 1.建议选择增强型服务&#xff1b; 2.不要加入体验改进计划。是否开启提示更新看你的想法&…

Nvidia Jetson 编解码开发(7)Jetpack 4.x版本Multimedia API 硬件编码开发--输出端对接ROS publish

1.前言 Nvidia Jetson 编解码开发(6)Jetpack 4.x版本Multimedia API 硬件编码开发--输入端对接Camera V4L2采集_free-xx的博客-CSDN博客 基于上篇基于开发 需求: (1)2路Camera采集 + H265编码 (2)2路编码完的H265数据通过ROS 发布出去,上位机播放 2. 开发记录 2…

【linux】2 make/Makefile和gitee

文章目录 一、Linux项目自动化构建工具-make/Makefile1.1 背景1.2 实例代码1.3 原理1.4 项目清理 二、linux下第一个小程序-进度条2.1 行缓冲区2.2 进度条 三、git以及gitee总结 ヾ(๑╹◡╹)&#xff89;" 人总要为过去的懒惰而付出代价ヾ(๑╹◡╹)&#xff89;" 一…

盛最多水的容器——力扣11

int maxArea(vector<int>& height) {int l=0, r=height.size()

半导体低压热氧工艺中的真空度精密控制解决方案

摘要&#xff1a;在目前的各种半导体材料热氧化工艺中&#xff0c;往往需要对正负压力进行准确控制并对温度变化做出快速的响应&#xff0c;为此本文提出了热氧化工艺的正负压力控制解决方案。解决方案的核心是基于动态平衡法分别对进气和排气流量进行快速调节&#xff0c;具体…

SpringBoot 微人事 职称管理模块(十三)

职称管理前端页面设计 在职称管理页面添加输入框 export default {name: "JobLevelMarna",data(){return{Jl:{name:""}}}}效果图 添加一个下拉框 v-model的值为当前被选中的el-option的 value 属性值 <el-select v-model"Jl.titlelevel" …

Spring cache整合Redis使用介绍

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…

【网络教程】如何创建/添加钉钉机器人以及如何获取机器人的Token/Secret

文章目录 创建钉钉机器人添加钉钉机器人获取机器人的Token/Secret相关网站创建钉钉机器人 这里以PC端的操作为例,按照如下操作进行 访问 钉钉开放平台选择机器人选项卡,点击右上角的创建应用,这里会有一个弹窗,我这里选择的是继续使用旧版,如图按照要求填写相关信息创建自…

第六次作业 运维高级 docker容器

1.安装docker服务&#xff0c;配置镜像加速器 卸载旧版本 yum remove docker docker-common docker-selinux docker-engine使用yum源安装 &#xff08;1&#xff09;安装Docker所需要的一些工具包 yum install -y yum-utils&#xff08;2&#xff09; 建立Docker仓库 (映射…

Java——它要求用户输入一个整数(实际上是一个字符串),然后计算该整数的平方值,并将结果输出。

这是一个Java程序&#xff0c;它要求用户输入一个整数&#xff08;实际上是一个字符串&#xff09;&#xff0c;然后计算该整数的平方值&#xff0c;并将结果输出。程序的基本流程如下&#xff1a; 首先&#xff0c;声明并初始化变量data和result&#xff0c;它们的初始值都为…

扁线电机定子转子工艺及自动化装备

售&#xff1a;扁线电机 电驱对标样件 需要请联&#xff1a;shbinzer &#xff08;拆车邦&#xff09; 新能源车电机路线大趋势&#xff0c;自动化装配产线需求迫切永磁同步电机是新能源车驱动电机的主要技术路线。目前新能源车上最广泛应用的类型为永磁同步电机&#xff0c…

W5500-EVB-PICO进行UDP组播数据回环测试(九)

前言 上一章我们用我们的开发板作为UDP客户端连接服务器进行数据回环测试&#xff0c;那么本章我们进行UDP组播数据回环测试。 什么是UDP组播&#xff1f; 组播是主机间一对多的通讯模式&#xff0c; 组播是一种允许一个或多个组播源发送同一报文到多个接收者的技术。组播源将…

如何搭建智能问答FAQ的底层数据基础呢?

搭建智能问答FAQ的底层数据基础是构建一个高效和准确的问答系统的关键。在这篇文章中&#xff0c;我们将探讨如何搭建智能问答FAQ的底层数据基础&#xff0c;并介绍需要注意的几个方面。 一、了解智能问答FAQ的概念和优势 智能问答FAQ是一种基于人工智能技术的问答系统&#…

Android进阶之路 - EditText输入字体自适应

遇到这么一个需求&#xff1a;“控件宽度有限&#xff0c;随着输入内容&#xff0c;动态修改字体大小”&#xff0c;如果是你&#xff0c;只如何来实现&#xff1f;又有几种方式&#xff1f; 嗯&#xff0c;就是这么一个简单的需求&#xff0c;让我记录了俩篇blog Android进阶…

ChatGPT提示与技巧分享:如何作出更好的提示2023年8月

​对ChatGPT的一些酷炫技巧感兴趣吗?这里提供了一些可以帮助你充分利用ChatGPT&#xff0c;成为AI工具专家的技巧。 毫无疑问&#xff0c;ChatGPT是目前最广泛使用的人工智能工具之一。它不仅毫不留情地取代了一些特定领域常用的软件小工具&#xff08;如智能对联、经典语录生…

ESP32应用教程(1)— VL53L3CX距离传感器

文章目录 前言 1 产品概述 1.1 技术规格 1.2 系统框图 1.3 设备引脚分布 2 工作流程 2.1 系统功能描述 2.2 状态机描述 2.3 测距模式说明 3 控制接口 3.1 设备地址 3.2 IC写1个字节数据 3.3 IC读1个字节数据 3.4 IC写多个字节数据 3.5 IC读多个字节数据 3.6 IC…

Nacos配置管理、Feign远程调用、Gateway服务网关

1.Nacos配置管理 1.1.将配置交给Nacos管理的步骤 1.在Nacos中添加配置 Data Id服务名称-环境名称.yaml eg&#xff1a;userservice-dev.yaml 2.引入nacos-config依赖 在user-service服务中&#xff0c;引入nacos-config的客户端依赖 <!--nacos配置管理依赖--> <dep…

Flink、Yarn架构,以Flink on Yarn部署原理详解

Flink、Yarn架构&#xff0c;以Flink on Yarn部署原理详解 Flink 架构概览 Apache Flink是一个开源的分布式流处理框架&#xff0c;它可以处理实时数据流和批处理数据。Flink的架构原理是其实现的基础&#xff0c;架构原理可以分为以下四个部分&#xff1a;JobManager、TaskM…