从导航图中获取参数

Get argument from navigation graph

我有一个导航图如下:

<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/main_graph"
    app:startDestination="@id/jobsFragment">

    <fragment
        android:id="@+id/jobsFragment"
        android:name=".JobsFragment"
        android:label="JobsFragment">

        <action
            android:id="@+id/action_jobsFragment_to_newJobDetailsActivity"
            app:destination="@id/newJobDetailsActivity" />

    </fragment>

    <fragment
        android:id="@+id/historyFragment"
        android:name=".HistoryFragment"
        android:label="HistoryFragment" />

    <fragment
        android:id="@+id/profileFragment"
        android:name=".ProfileFragment"
        android:label="ProfileFragment" />

    <activity
        android:id="@+id/newJobDetailsActivity"
        android:name=".NewJobDetailsActivity"
        android:label="activity_new_job_details"
        tools:layout="@layout/activity_new_job_details">

        <!-- This is what I want a reference to -->
        <argument
            android:name="jobId"
            app:argType="string" />

    </activity>

</navigation>

目标:以编程方式检索 newJobDetailsActivityargument namejobId 的值,无需引用到 action。使用当前的 API,我可以通过 action 设置 jobId 而无需知道字符串名称文字。这很好,但对于我希望调用者能够导航到目的地而无需知道调用者附加到哪个组件的情况,它并不理想。这更容易用代码解释。让我们来看看我试图解决的两个场景。

场景A

假设我有一个 RecyclerView.ViewHolder,其中有一些代码可以从名为 JobsFragmentFragment 导航到名为 NewJobDetailsActivityActivity点击了。

itemView.setOnClickListener {
    itemView.findNavController().navigate(
        JobsFragmentDirections.ActionJobsFragmentToNewJobDetailsActivity(jobId)
    )
}

如果 RecyclerView.ViewHolder 仅存在于 JobsFragment 中,这很好,但如果我想在将来从其他 FragmentActivity 中重用它,我的代码失败了,因为视图持有者需要引用 JobsFragmentDirectionsaction 才能传递 jobId 参数。

场景B

假设我需要为通知创建 PendingIntent,并且我想使用导航架构 API。

NavDeepLinkBuilder(context)
    .setGraph(R.navigation.main_graph)
    .setDestination(R.id.newJobDetailsActivity)
    .setArguments(Bundle().apply {
        putString("jobId", jobId)
    })
    .createPendingIntent()

在这种情况下,我不需要对 action 的引用,但情况实际上更糟,因为我找不到对 argument name 的引用在 main_graph 中,所以我不得不使用字符串文字来指定它。呸

tl;dr:有人知道改善这两种情况的方法吗?有没有办法以编程方式从导航图中获取对 argument 的引用?

依赖项android.arch.navigation:navigation-fragment-ktx:1.0.0-alpha06android.arch.navigation:navigation-ui-ktx:1.0.0-alpha06

插件: androidx.navigation.safeargs

对于第二种情况,您可以使用如下内容:

val args = JobsFragmentArgs.Builder(id).build().toBundle()
NavDeepLinkBuilder(context)
    .setGraph(R.navigation.main_graph)
    .setDestination(R.id.newJobDetailsActivity)
    .setArguments(args)
    .createPendingIntent()

对于第一种情况,就我个人而言,我建议从创建适配器的 fragment/activity 传递单击侦听器并将侦听器设置为绑定时的视图持有者。然后你可以为其他 fragments/activities.

以不同的方式实现点击

正如 Emre 在他的回答中提到的那样,有一个 args 构建器可以从生成的目标 *Args class 调用,然后可以使用参数构建并转换为 Bundle。实现它的方法不是最漂亮的,但它实现了我想要实现的目标。更新的实现:

场景A

itemView.setOnClickListener {
    itemView.findNavController().navigate(
        R.id.newJobDetailsActivity,
        NewJobDetailsActivityArgs.Builder(jobId)
            .build()
            .toBundle()
    )
}

场景B

NavDeepLinkBuilder(context)
    .setGraph(R.navigation.main_graph)
    .setDestination(R.id.newJobDetailsActivity)
    .setArguments(
        NewJobDetailsActivityArgs.Builder(jobId)
            .build()
            .toBundle()
    )
    .createPendingIntent()

更新:使用NavDeepLinkBuilder构建的PendingIntent点击通知实际上不起作用。参数未传递到目标 Activity。想知道这是否是导航框架的错误。跟踪此问题 here