Jetpack Navigation 在 fragment 间跳转时使用 Safe Args 传参数

更新日期: 2023-05-25 阅读次数: 1116 字数: 788 分类: Android

在一个 Android APP 中,需要由 Fragment A 跳转到 B 时进行传参,官方推荐使用 Safe Args。 Safe Args 主打的就是类型安全 (type-safety),测试了一下。

配置 build.gradle

SafeArgs 和导航组件的其它模块不太一样,它本身并不是一个 API,而是一个可以生成代码的 gradle 插件。 所以需要将它设置为 gradle 依赖,并且在构建时使其能够正确运行来生成所需的代码。

首先,在项目级 project build.gradle 中增加配置:

buildscript {
    dependencies {
        def nav_version = "2.5.2"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }
}

注意:

  • 需要放到 plugins 部分之上。如果放在下方会报错。
  • 版本号需要跟 app build.gradle 中的 navigation 依赖保持一致

app build.gradle 中增加:

plugins {
  id 'androidx.navigation.safeargs.kotlin'
}

如果是纯 kotlin 模块的话可以这样配置,如果是 java 的话,去掉 kotlin 就行了。 然后同步一下 gradle。

吐槽:Google 搞这些玩意,配置太复杂了。

Navigation XML 中定义参数

Android Studio 中打开 navigation xml 文件,点击目标 fragment (即,要跳转到的目标 fragment)。

在 Design 视图下,可以看到 Arguments 区域,点击加号,在弹出的 Add Argument 窗口中配置就可以了,这里倒是挺人性化的。

safe args 配置

添加之后,会看到 XML 中对应 fragment 自动增加了 argument 配置:

<fragment
	android:id="@+id/nav_edit"
	android:name="com.sunzhongwei.someapp.ui.edit.EditFragment"
	android:label="@string/menu_edit"
	tools:layout="@layout/fragment_edit" >
	<argument
		android:name="id"
		app:argType="long"
		android:defaultValue="0L" />
</fragment>

自动生成代码

此时执行 build 操作,gradle 就会针对上面配置的参数,生成相应的代码。 否则 Android Studio 没法完成后面代码的自动提示。

由 Android 视图切换到 Project 视图 (麻烦),才能看到生成的代码,在目录:

app/build/generated/source/navigation-args

假设是由 HomeFragment 跳转到 EditFragment, 那么生成的主要有两个文件:

  • EditFragmentArgs: 用于解析 args。
  • HomeFragmentDirections: 用于设置 args。基于 action 生成,即,navigation xml 中两个 fragment 间的连线。

HomeFragmentDirections.kt

public class HomeFragmentDirections private constructor() {
  private data class ActionNavHomeToNavEdit(
    public val id: Long = 0L
  ) : NavDirections {
    public override val actionId: Int = R.id.action_nav_home_to_nav_edit

    public override val arguments: Bundle
      get() {
        val result = Bundle()
        result.putLong("id", this.id)
        return result
      }
  }

  // 注意这个 object,传递 args 就是通过这个地方
  public companion object {
    public fun actionNavHomeToNavEdit(id: Long = 0L): NavDirections = ActionNavHomeToNavEdit(id)
  }
}

EditFragmentArgs.kt:

public data class EditFragmentArgs(
  public val id: Long = 0L
) : NavArgs {
  public fun toBundle(): Bundle {
    val result = Bundle()
    result.putLong("id", this.id)
    return result
  }

  public fun toSavedStateHandle(): SavedStateHandle {
    val result = SavedStateHandle()
    result.set("id", this.id)
    return result
  }

  ...
}

设置 args 参数,并跳转

例如,在 RecyclerView 列表中,点击一个子项,跳转详情页

binding.root.setOnClickListener {
	val action = HomeFragmentDirections.actionNavHomeToNavEdit(item.id)
	it.findNavController().navigate(action)
}

解析 args 参数

例如,跳转目标是 EditFragment,那么在 EditFragment 中:

private val args: EditFragmentArgs by navArgs()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
	super.onViewCreated(view, savedInstanceState)

	val id = args.id
	println(id)

	// ...
}

其他

  • safe args 是否可以传递对象?
  • 太麻烦了,真不如用 bundle。为了个类型安全,完全不值得。官方的说法是,只在无法使用 gradle 按照 safe args 插件时,才推荐使用 bundle 传递参数。

参考

  • 注意不要参考 google 中文的文档,已经过时了。需要参考官方的英文文档:https://developer.android.com/guide/navigation/navigation-pass-data
  • 谷歌开发者公众号文章中的介绍:https://mp.weixin.qq.com/s/sEUkAQMwxqEn16O4SfXZ1Q

tags: Jetpack Navigation

关于作者 🌱

我是来自山东烟台的一名开发者,有敢兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式