I have a nav graph as follows:
<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>
Goal: retrieve the value of newJobDetailsActivity
's argument
name
, jobId
, programmatically without needing a reference to an action
. With the current API, I can set the jobId
through an action
without needing to know the string name literal. This is good, but it's not ideal for cases when I want the caller to be able to navigate to a destination without needing to know which component the caller is attached to. This is easier to explain with code. Let's take a look at two scenarios that I'm trying to solve for.
Scenario A
Let's assume I have a RecyclerView.ViewHolder
that has some code in it that navigates from a Fragment
called JobsFragment
to an Activity
called NewJobDetailsActivity
when clicked on.
itemView.setOnClickListener {
itemView.findNavController().navigate(
JobsFragmentDirections.ActionJobsFragmentToNewJobDetailsActivity(jobId)
)
}
This is great if the RecyclerView.ViewHolder
only ever exists in the JobsFragment
, but if I want to reuse it from an additional Fragment
or Activity
in the future, my code flops because the view holder needs to reference JobsFragmentDirections
's action
in order to pass the jobId
argument.
Scenario B
Let's assume I need to create a PendingIntent
for a notification, and I want to do so using the Navigation Architecture API.
NavDeepLinkBuilder(context)
.setGraph(R.navigation.main_graph)
.setDestination(R.id.newJobDetailsActivity)
.setArguments(Bundle().apply {
putString("jobId", jobId)
})
.createPendingIntent()
In this case, I don't need a reference to an action
, but the situation is actually worse because I can't find a reference to the argument
name
in the main_graph
, so I'm forced to specify it using a string literal. Yuck.
tl;dr: Is anyone aware of a way to improve these two scenarios? Is there a way to programmatically get a reference to an argument
from a nav graph?
Dependencies: android.arch.navigation:navigation-fragment-ktx:1.0.0-alpha06
, android.arch.navigation:navigation-ui-ktx:1.0.0-alpha06
Plugin: androidx.navigation.safeargs
For the second scenario you can use something like this:
val args = JobsFragmentArgs.Builder(id).build().toBundle()
NavDeepLinkBuilder(context)
.setGraph(R.navigation.main_graph)
.setDestination(R.id.newJobDetailsActivity)
.setArguments(args)
.createPendingIntent()
For the first scenario, personally, I would recommend passing the on click listener from the fragment/activity that creates the adapter and setting the listener to the view holder on bind. Then you can implement on click differently for other fragments/activities.