持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第13天,点击查看活动详情
概述
通过前面几篇笔记的学习,我们已经能够基本使用导航组件中比较重要的功能了。对于单Activity
多Fragment
的应用来说,通过导航组件来统一管理项目中的页面跳转可以帮助我们管理导航的逻辑,我们不再需要书写很多和supportFragmentManager
相关的代码,只需要明确应该在什么时候执行什么样的导航即可。
这篇学习笔记的目的是学习在目的地之间传递数据,之前知识简单了解了使用argument
标签可以定义我们需要在目的地之间传递的数据,通过这篇笔记将会明确如何在目的地之间传递数据,能够传递什么样的数据以及需要注意的一些操作。
定义目的地参数
如果我们定义的目的地需要接收其它目的地提供的数据,我们可以在目的地中提前定义好需要的数据类型以及字段名称,当使用导航组件的时候会自动帮我们组装数据,下面的代码演示了我们的一个目的地需要接收一些数据,如下所示:
<fragment
android:id="@+id/fragment_transfer_data_first"
android:name="com.zyf.navstudy.nav_data.TransferDataFirstFragment"
android:label="@string/transfer_data_first_fragment"
tools:layout="@layout/fragment_transfer_data_first"
>
<argument
android:name="type"
app:argType="integer"
app:nullable="false"
android:defaultValue="-1"
/>
<argument
android:name="name"
app:argType="string"
app:nullable="true"
/>
</fragment>
复制代码
在上面的代码中,TransferDataFirstFragment
这个目的地能够接收两个参数,分别是integer
类型的type
和string
类型的name
,其中type
不能为空并且默认值为-1
,name
可以为空,我们会在进入到TransferDataFirstFragment
目的地之后打印出这两个数据。
下面是进入这个目的地时传递的信息以及打印出的信息:
//打开目的地的时候不传递任何信息则打印的信息如下:
type is -1,name is null
//打开目的地的时候传递指定的信息:
findNavController().navigate(
R.id.to_transfer_data_first,
bundleOf("type" to 100, "name" to "test")
)
//打印的信息如下:
type is 100,name is test
复制代码
从上面的输出信息可以看到,我们设置的信息能够被正确传递。需要注意的是:我们在导航图中设置了某一个参数的值不能为空,那么在传递数据的时候就一定要保证这个数据不为空,否则将会抛出异常。
上面我们传递了integer
和string
两种类型的参数,在定义参数的时候导航组件指出我们定义更多的参数类型,如下表所示:
类型 | argType类型名称 | 是否支持默认值 | 是否由路由处理 | 是否可空 |
---|---|---|---|---|
整数 | argType="integer" | 是 | 是 | 否 |
浮点数 | argType="float" | 是 | 是 | 否 |
长整数 | argType="long" | 是(默认值必须以L结尾) | 是 | 否 |
布尔值 | argType="boolean" | 是(true)或false | 是 | 否 |
字符串 | argType="string" | 是 | 是 | 是 |
资源引用 | argType="reference" | 是(默认值格式为:@resourceType/resourceName或0) | 是 | 否 |
Parcelable | argType="类的完全限定名" | 只支持默认值设置为@null ,不支持其它默认值 |
否 | 是 |
Serializable | argType="类的完全限定名" | 只支持默认值设置为@null ,不支持其它默认值 |
否 | 是 |
Enum | argType="类的完全限定名" | 是(默认值必须与非限定名称匹配) | 否 | 否 |
除了上面表格中的参数类型,还可以传递数组类型,但是在传递数组类型的时候需要注意以下问题:
- 不支持枚举数组和资源引用数组
- 无论基础类型是否支持可为
null
值,数组都支持可为null
值,例如:app:argType="integer[]"
指定整型数组的时候,同时可以指定app:nullable="true"
来声明数组可空 - 数组只能使用
@null
作为默认值,不支持其它默认值
替换操作中的目的地参数
我们在目的地中定义了可以接收的参数,有时候我们的参数和我们执行导航的操作是密切相关的,对于导航到同一个目的地的不同操作携带的数据是不一样的,这种时候,我们就可以在操作中直接指定数据,如下所示:
<action
android:id="@+id/to_transfer_data_first"
app:destination="@id/fragment_transfer_data_first"
app:enterAnim="@anim/anim_fragment_enter"
app:exitAnim="@anim/anim_fragment_exit"
app:popEnterAnim="@anim/anim_fragment_pop_enter"
app:popExitAnim="@anim/anim_fragment_pop_exit"
>
<argument
android:name="type"
app:argType="integer"
android:defaultValue="10"
/>
<argument
android:name="name"
app:argType="string"
android:defaultValue="demo"
/>
</action>
复制代码
可以看到,同样是刚才的那个目的地,这次我们在操作上直接指定了数据,而不在导航发生时指定数据,下面是打印的信息:
type is 10,name is demo
复制代码
需要注意的是:如果我们同时在导航图和导航发生时的代码上指定的数据,那么最终将会以我们在代码中指定的数据为准。
将数据传递给起始目的地
有时候我们可能需要向起始目的地传递一些参数,比如通过通知或者应用微件打开应用的时候,我们通常会依赖其传递的数据执行不同的操作。
向起始目的地传递数据的方式有两种:
- 如果我们会自行创建
NavHost
,那么可以调用NavHostFragment.create(R.navigation.nav_graph,args)
,来传递数据,其中args
就是保存数据的Bundle
- 如果我们使用
NavHostFragment
作为导航宿主,那么就可以使用NavController.setGraph(R.navigation.nav_graph,args)
来传递数据,其中args
就是保存数据的Bundle
如果我们使用上面的第二种方式传递数据,则还需要注意以下内容:
- 不能在
onCreate()
直接使用findNavController()
来获得NavController
对象,这个在第一篇笔记中已经说过了 - 不能在XML中使用
NavHostFragment
传递app:navGraph
属性,因为这会在不使用任何参数的情况下内部调用setGraph()
,导致图标和起始目的地被创建两次。
下面的代码演示了使用第二种方式向起始目的地传递数据:
- 在导航图中定义起始目的地可以接收的参数:
<argument
android:name="fromAppWidget"
app:argType="boolean"
android:defaultValue="false"
app:nullable="false"
/>
复制代码
- 在
Activity
中设置数据
//需要传递给起始目的地的数据
val bundle = bundleOf("fromAppWidget" to true)
(supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment).findNavController()
.setGraph(R.navigation.nav_home, bundle)
复制代码
- 在起始目的地中获取数据并打印:
Logs.e("NavHomeFragment","isFromAppWidget:${arguments?.getBoolean("fromAppWidget")}")
复制代码
最终打印的数据如下:
isFromAppWidget:true
复制代码
可以看到:我们成功向起始目的地中传递了参数并在起始目的地获得了设置的数据。