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

文章目录

    在一个 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

    关于作者 🌱

    我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊,或者关注我的个人公众号“大象工具”, 查看更多联系方式