Navigation 支持您通过定义目的地参数将数据附加到导航操作。例如,用户个人资料目的地可能会根据用户 ID 参数来确定要显示哪个用户。
通常情况下,强烈建议您仅在目的地之间传递最少量的数据。例如,您应该传递键来检索对象而不是传递对象本身,因为在 Android 上用于保存所有状态的总空间是有限的。如果您需要传递大量数据,不妨考虑使用 ViewModel(如在 Fragment 之间共享数据中所述)。
定义目的地参数
如需在目的地之间传递数据,首先请按照以下步骤将参数添加到接收它的目的地来定义参数:
在 Attributes 面板中,点击 Add (+)。
在显示的 Add Argument Link 窗口中,输入参数名称、参数类型、参数是否可为 null,以及默认值(如果需要)。
点击 Add。请注意,该参数现在会显示在 Attributes 面板的 Arguments 列表中。
接下来,点击会将您转到此目的地的相应操作。在 Attributes 面板中,您现在应该会在 Argument Default Values 部分中看到新添加的参数。
您还可以看到该参数已添加到 XML 中。点击 Text 标签页以切换到 XML 视图,就会发现您的参数已添加到接收该参数的目的地。相关示例如下所示:
android:name="myArg"
app:argType="integer"
android:defaultValue="0" />
支持的参数类型
Navigation 库支持以下参数类型:
类型
app:argType 语法
是否支持默认值?
是否支持 null 值?
整数
app:argType="integer"
是
否
浮点数
app:argType="float"
是
否
长整数
app:argType="long"
是 - 默认值必须始终以“L”后缀结尾(例如“123L”)。
否
布尔值
app:argType="boolean"
是 -“true”或“false”
否
字符串
app:argType="string"
是
是
资源引用
app:argType="reference"
是 - 默认值必须为“@resourceType/resourceName”格式(例如,“@style/myCustomStyle”)或“0”
否
自定义 Parcelable
app:argType="",其中 是 Parcelable 的完全限定类名称
支持默认值“@null”。不支持其他默认值。
是
自定义 Serializable
app:argType="",其中 是 Serializable 的完全限定类名称
支持默认值“@null”。不支持其他默认值。
是
自定义 Enum
app:argType="",其中 是 Enum 的完全限定名称
是 - 默认值必须与非限定名称匹配(例如,“SUCCESS”匹配 MyEnum.SUCCESS)。
否
注意:仅在引用类型中支持对资源的引用。在其他任何类型中使用资源引用都会导致异常。
如果参数类型支持 null 值,您可以使用 android:defaultValue="@null" 声明默认值 null。
如果您选择其中一种自定义类型,则系统会显示 Select Class 对话框,提示您选择与该类型对应的类。您可以通过 Project 标签页从当前项目中选择类。
您可以选择 ,让 Navigation 库根据提供的值来确定类型。
您可以选中 Array,以指明参数应该是所选 Type 值的数组。请注意以下几点:
不支持枚举数组和资源引用数组。
无论基础类型是否支持可为 null 的值,数组都支持可为 null 的值。例如,使用 app:argType="integer[]" 时,您可以使用 app:nullable="true" 来指示可传递 null 数组。
数组仅支持一个默认值,即“@null”。数组不支持其他任何默认值。
替换操作中的目的地参数
所有导航至目的地的操作都使用目的地级的参数和默认值。如果需要,您可以通过在操作级定义参数来替换参数的默认值(如果不存在,则设置一个)。此参数必须与目的地中声明的参数具有相同的名称和类型。
以下 XML 展示了如何声明操作并替换上例中的目的地级参数:
app:destination="@+id/myFragment">
android:name="myArg"
app:argType="integer"
android:defaultValue="1" />
使用 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.5"
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() 方法检索参数。
以下示例说明了如何使用这些方法来设置参数并将其传递给 navigate() 方法:
Kotlin
override fun onClick(v: View) {
val amountTv: EditText = view!!.findViewById(R.id.editTextAmount)
val amount = amountTv.text.toString().toInt()
val action = SpecifyAmountFragmentDirections.confirmationAction(amount)
v.findNavController().navigate(action)
}Java
@Override
public void onClick(View view) {
EditText amountTv = (EditText) getView().findViewById(R.id.editTextAmount);
int amount = Integer.parseInt(amountTv.getText().toString());
ConfirmationAction action =
SpecifyAmountFragmentDirections.confirmationAction();
action.setAmount(amount);
Navigation.findNavController(view).navigate(action);
}
在接收目的地的代码中,请使用 getArguments() 方法来检索 bundle 并使用其内容。使用 -ktx 依赖项时,Kotlin 用户还可以使用 by navArgs() 属性委托来访问参数。
Kotlin
val args: ConfirmationFragmentArgs by navArgs()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val tv: TextView = view.findViewById(R.id.textViewAmount)
val amount = args.amount
tv.text = amount.toString()
}Java
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
TextView tv = view.findViewById(R.id.textViewAmount);
int amount = ConfirmationFragmentArgs.fromBundle(getArguments()).getAmount();
tv.setText(amount + "");
}
将 Safe Args 用于全局操作
将 Safe Args 用于全局操作时,您必须为根 元素提供一个 android:id 值,如以下示例中所示:
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_nav"
app:startDestination="@id/mainFragment">
...
Navigation 会根据 android:id 值为 元素生成一个 Directions 类。例如,如果您有具有 android:id=@+id/main_nav 的 元素,则生成的类的名称为 MainNavDirections。 元素中的所有目的地都生成了方法,以使用前一节中所述的相同方法来访问所有关联的全局操作。
使用 Bundle 对象在目的地之间传递参数
如果您不使用 Gradle,仍然可以使用 Bundle 对象在目的地之间传递参数。创建 Bundle 对象并使用 navigate() 将它传递给目的地,如下所示:
Kotlin
val bundle = bundleOf("amount" to amount)
view.findNavController().navigate(R.id.confirmationAction, bundle)Java
Bundle bundle = new Bundle();
bundle.putString("amount", amount);
Navigation.findNavController(view).navigate(R.id.confirmationAction, bundle);
在接收目的地的代码中,请使用 getArguments() 方法来检索 Bundle 并使用其内容:
Kotlin
val tv = view.findViewById(R.id.textViewAmount)
tv.text = arguments?.getString("amount")Java
TextView tv = view.findViewById(R.id.textViewAmount);
tv.setText(getArguments().getString("amount"));
将数据传递给起始目的地
您可以将数据传递给应用的起始目的地。首先,您必须显式构建一个 Bundle 来存储数据。然后,使用以下方法之一将该 Bundle 传递给起始目的地:
注意:手动调用包含参数的 setGraph() 时,您在 XML 中创建 NavHostFragment 时不得使用 app:navGraph 属性,因为这会在不使用任何参数的情况下内部调用 setGraph(),导致您的图表和起始目的地被创建两次。
ProGuard 注意事项
如果要缩减代码,您需要防止在缩减过程中混淆 Parcelable、Serializable 和 Enum 类名称。您可以通过以下两种方式来实现此目的:
使用 @Keep 注解
以下示例说明了如何在模型类定义中添加 @Keep 注解:
Kotlin
@Keep class ParcelableArg : Parcelable { ... }
@Keep class SerializableArg : Serializable { ... }
@Keep enum class EnumArg { ... }Java
@Keep public class ParcelableArg implements Parcelable { ... }
@Keep public class SerializableArg implements Serializable { ... }
@Keep public enum EnumArg { ... }
使用 keepnames 规则
您也可以将 keepnames 规则添加到您的 proguard-rules.pro 文件中,如以下示例中所示:
proguard-rules.pro
...
-keepnames class com.path.to.your.ParcelableArg
-keepnames class com.path.to.your.SerializableArg
-keepnames class com.path.to.your.EnumArg
...
其他资源
如需详细了解 Navigation,请参阅下面列出的其他资源:
示例
Codelab
视频