Safe Args简介
Navigation 组件具有一个名为 Safe Args 的 Gradle 插件,该插件可以生成简单的 object 和 builder 类,以便以类型安全的方式浏览和访问任何关联的参数。我们强烈建议您将 Safe Args 用于导航和数据传递,因为它可以确保类型安全。
在某些情况下您无法使用 Safe Args 插件,例如当您不使用 Gradle 时。在这些情况下,您可以使用 Bundle 直接传递数据。
如需将 Safe Args 添加到您的项目中,请在顶层 build.gradle 文件中包含以下 classpath:
buildscript {
repositories {
google()
}
dependencies {
def nav_version = "2.3.2"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
}
}
您还必须应用以下两个可用插件之一。
如需生成适用于 Java 模块或 Java 和 Kotlin 混合模块的 Java 语言代码,请将以下行添加到应用或模块的 build.gradle 文件中:
apply plugin: "androidx.navigation.safeargs"
此外,如需生成适用于 Kotlin 独有的模块的 Kotlin 代码,请添加以下行:
apply plugin: "androidx.navigation.safeargs.kotlin"
根据迁移到 AndroidX 文档,您的 gradle.properties 文件中必须具有 android.useAndroidX=true。
启用 Safe Args 后,生成的代码会为每个操作包含以下类型安全的类和方法,以及每个发送和接收目的地。
-
为生成操作的每一个目的地创建一个类。该类的名称是在源目的地的名称后面加上“Directions”。例如,如果源目的地是名为 SpecifyAmountFragment 的 Fragment,则生成的类的名称为 SpecifyAmountFragmentDirections。
该类会为源目的地中定义的每个操作提供一个方法。 -
对于用于传递参数的每个操作,都会创建一个 inner 类,该类的名称根据操作的名称确定。例如,如果操作名称为 confirmationAction,,则类名称为 ConfirmationAction。如果您的操作包含不带 defaultValue 的参数,则您可以使用关联的 action 类来设置参数值。
-
为接收目的地创建一个类。该类的名称是在目的地的名称后面加上“Args”。例如,如果目的地 Fragment 的名称为 ConfirmationFragment,,则生成的类的名称为 ConfirmationFragmentArgs。可以使用该类的 fromBundle() 方法检索参数。
使用前准备
如官方文档所言,首先需要将Safe Args插件添加到项目中,并应用相关插件。
本文选择生成适用于Java模块的Java语言代码。添加插件的写法与文档有些不同,在build.gradle中这样添加:
plugins {
id 'com.android.application'
id 'androidx.navigation.safeargs'
}
阅读启用Safe Args后的说明,可以得知,启用该插件后,插件将根据目标(Destination)名和操作(Action)名来生成一些类和方法。因此,为了避免生成的类名和方法名过于复杂,需要对导航图进行调整,比如修改了action的id(自动生成的id太长了,会导致插件生成的类名和方法名同样很长)。
<?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/mobile_navigation"
app:startDestination="@+id/navigation_home">
<fragment
android:id="@+id/navigation_home"
android:name="com.month.bottomnavigationdemo.ui.home.HomeFragment"
android:label="@string/title_home"
tools:layout="@layout/fragment_home" >
<action
android:id="@+id/action_jump"
app:destination="@id/navigation_notifications">
<argument
android:name="param" />
</action>
</fragment>
<fragment
android:id="@+id/navigation_notifications"
android:name="com.month.bottomnavigationdemo.ui.notifications.NotificationsFragment"
android:label="@string/title_notifications"
tools:layout="@layout/fragment_notifications" >
<argument
android:name="param"
app:argType="string"
android:defaultValue="unknown" />
</fragment>
</navigation>
此处只展示了导航图中HomeFragment和NotificationsFragment的部分。请关注以下几个点:
- HomeFragment中存在一个id为"action_jump",目标是NotificationFragment的action,且附带了一个名为“param”的参数,没有设置默认值。
- NotificationsFragment中存在一个名为“param”的参数,类型为“String”,默认值为“unknown”。
自动生成代码
完成上述操作后,Rebuild Project,Safe Args将自动生成相关的代码。
可以看到,插件为我们生成了HomeFragmentDirections类以及NotificationsFragmentArgs类。Directions类对应行为,而Args类对应参数。由于导航图中只有HomeFragment设置了行为(action),同样也只有NotificationsFragment设置了参数(argument),因此只生成了这两个类,而没有生成HomeFragmentArgs类和NotificationsFragmentDirections类。
传递数据
有了以上这些类,就可以使用它们帮助我们传递数据了。
进入HomeFragment,修改按钮的点击事件,传递数据的方式有几种,下面分别介绍。
一、在Action中携带数据
首先获取到对应的Action,这个类的类名与导航图中action的id存在对应关系,并且由于没有设置默认值,生成的构造方法中要求填入一个对应类型的值。
//没有设置默认值的情况,需要填入参数
HomeFragmentDirections.ActionJump action = HomeFragmentDirections.actionJump("hello world!");
//设置了默认值,构造方法无参
//HomeFragmentDirections.ActionJump action = HomeFragmentDirections.actionJump();
除此之外,也可以使用set方法对对应的参数进行赋值。
action.setParam("bye~ world!");
最后通过NavController.navigate方法进行导航。
//HomeFragment
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Button button = getView().findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
HomeFragmentDirections.ActionJump action = HomeFragmentDirections.actionJump("hello world!");
action.setParam("bye~ world!");
NavController navController = Navigation.findNavController(v);
navController.navigate(action);
}
});
}
二、使用Bundle
这里的Bundle通过Args类的建造者模式创建,使用方法如下:
Bundle bundle = new NotificationsFragmentArgs.Builder().setParam("bye~ world!").build().toBundle();
完整的代码:
//HomeFragment
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Button button = getView().findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Bundle bundle = new NotificationsFragmentArgs.Builder().setParam("bye~ world!").build().toBundle();
NavController navController = Navigation.findNavController(v);
navController.navigate(R.id.action_jump, bundle);
}
});
}
获取数据
与传递数据相反,只要调用对应的Get方法即可。
//NotificationFragment
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//获取参数
if (getArguments() != null) {
String param = NotificationsFragmentArgs.fromBundle(getArguments()).getParam();
notificationsViewModel.setText(param);
}
}