来自 Notification 的深层链接 - 如何通过后台堆栈传递数据?
Deep linking from Notification - how to pass data back up through the backstack?
-
android
-
android-architecture-components
-
android-architecture-navigation
-
android-deep-link
-
android-navigation-graph
在我的应用中,用户可以 select 一个类别,然后 select 该类别中的一个项目,最后查看项目详细信息。 standard/forward 流程是:
SelectCategoryFragment
-> SelectItemFragment
-> ViewItemDetailsFragment
在 select 类别中,selectedCatId
通过 Bundle
从 SelectCategoryFragment
传递到 SelectItemFragment
:
NavController navController = Navigation.findNavController(v);
Bundle args = new Bundle();
args.putLong(SelectItemFragment.ARG_CATEGORY_ID, selectedCatId);
navController.navigate(R.id.action_nav_categories_to_items, args);
SelectItemFragment
将使用 getArguments().getLong(ARG_CATEGORY_ID)
值查询并显示 selected 类别中的相应项目。
效果很好。但我现在正尝试在用户点击通知时实施深度链接,将他们直接跳转到 ViewItemDetailsFragment
,并使用可以将他们带到 SelectItemFragment
,然后 SelectCategoryFragment
.
我的问题是,如上所述,SelectItemFragment
取决于传递给它的 ARG_CATEGORY_ID
参数,以便 retrieve/display 它的数据。我已经阅读了 deep linking and nested navigation graphs,但真的不知道如何通过 ARG_CATEGORY_ID
和深度 linking/backstacks。
有没有一种简洁的方法可以在用户按下时将数据从 ViewItemDetailsFragment
传递到 SelectItemFragment
?
TLDR:问题可以使用嵌套图来解决。请参阅此 article 了解更多详情。
更长的答案
让我们定义简单的片段 FragmentRed
、FragmentGreen
和 FragmentBlue
,它们用相应的背景颜色填充简单的布局:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_red_dark">
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
并这样声明片段类:
class FragmentRed : Fragment() {
private val args: FragmentRedArgs by navArgs()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.red, container, false)
view.findViewById<TextView>(R.id.textView).text = args.foo.toString()
return view
}
}
FragmentGreen
和 FragmentBlue
是复制粘贴的,但是用相应的颜色文本替换了所有颜色文本,即 FragmentRedArgs
-> FragmentBlueArgs
,R.layout.red
- > R.layout.blue
.
让我们这样声明主要 activity 布局:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:id="@+id/content"
android:layout_height="match_parent">
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/main_graph" />
</FrameLayout>
其中 main_graph
是:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_graph"
app:startDestination="@id/fragment_red">
<fragment
android:id="@+id/fragment_red"
android:name="com.playground.FragmentRed">
<argument
android:name="foo"
android:defaultValue="0"
app:argType="integer" />
<action
android:id="@+id/action_fragment_red_to_fragment_green"
app:destination="@id/fragment_green" />
</fragment>
<navigation
android:id="@+id/secondLevel"
app:startDestination="@id/fragment_green">
<fragment
android:id="@+id/fragment_green"
android:name="com.playground.FragmentGreen">
<argument
android:name="bar"
android:defaultValue="0"
app:argType="integer" />
<action
android:id="@+id/action_fragment_green_to_fragment_blue"
app:destination="@id/fragment_blue" />
</fragment>
<fragment
android:id="@+id/fragment_blue"
android:name="com.playground.FragmentBlue">
<argument
android:name="zar"
android:defaultValue="0"
app:argType="integer" />
</fragment>
</navigation>
</navigation>
现在在 MainActivity
让我们生成一个新通知,为每个片段传递参数:"foo"(红色)- 1,"bar"(绿色)- 2,"zar"(蓝色)- 3.
我们的期望是点击通知打开带有文本 3 的蓝屏,在返回点击后看到带有 2 的绿屏,再次点击应该在屏幕上显示带有 1 的红屏:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navController = findNavController(R.id.nav_host_fragment)
val pendingIntent = navController.createDeepLink()
.setGraph(R.navigation.main_graph)
.setDestination(R.id.fragment_blue)
.setArguments(bundleOf("foo" to 1, "bar" to 2, "zar" to 3))
.createPendingIntent()
createNotificationChannel() // outside of the scope of this answer
val builder = NotificationCompat.Builder(this, "my_channel")
.setContentTitle("title")
.setContentText("content text")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setSmallIcon(R.drawable.android)
.setAutoCancel(true)
.setChannelId("channelId")
with(NotificationManagerCompat.from(this)) {
notify(100, builder.build())
}
}
}
这是设备上的实际行为:
android
android-architecture-components
android-architecture-navigation
android-deep-link
android-navigation-graph
在我的应用中,用户可以 select 一个类别,然后 select 该类别中的一个项目,最后查看项目详细信息。 standard/forward 流程是:
SelectCategoryFragment
->SelectItemFragment
->ViewItemDetailsFragment
在 select 类别中,selectedCatId
通过 Bundle
从 SelectCategoryFragment
传递到 SelectItemFragment
:
NavController navController = Navigation.findNavController(v);
Bundle args = new Bundle();
args.putLong(SelectItemFragment.ARG_CATEGORY_ID, selectedCatId);
navController.navigate(R.id.action_nav_categories_to_items, args);
SelectItemFragment
将使用 getArguments().getLong(ARG_CATEGORY_ID)
值查询并显示 selected 类别中的相应项目。
效果很好。但我现在正尝试在用户点击通知时实施深度链接,将他们直接跳转到 ViewItemDetailsFragment
,并使用可以将他们带到 SelectItemFragment
,然后 SelectCategoryFragment
.
我的问题是,如上所述,SelectItemFragment
取决于传递给它的 ARG_CATEGORY_ID
参数,以便 retrieve/display 它的数据。我已经阅读了 deep linking and nested navigation graphs,但真的不知道如何通过 ARG_CATEGORY_ID
和深度 linking/backstacks。
有没有一种简洁的方法可以在用户按下时将数据从 ViewItemDetailsFragment
传递到 SelectItemFragment
?
TLDR:问题可以使用嵌套图来解决。请参阅此 article 了解更多详情。
更长的答案
让我们定义简单的片段 FragmentRed
、FragmentGreen
和 FragmentBlue
,它们用相应的背景颜色填充简单的布局:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_red_dark">
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
并这样声明片段类:
class FragmentRed : Fragment() {
private val args: FragmentRedArgs by navArgs()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.red, container, false)
view.findViewById<TextView>(R.id.textView).text = args.foo.toString()
return view
}
}
FragmentGreen
和 FragmentBlue
是复制粘贴的,但是用相应的颜色文本替换了所有颜色文本,即 FragmentRedArgs
-> FragmentBlueArgs
,R.layout.red
- > R.layout.blue
.
让我们这样声明主要 activity 布局:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:id="@+id/content"
android:layout_height="match_parent">
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/main_graph" />
</FrameLayout>
其中 main_graph
是:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_graph"
app:startDestination="@id/fragment_red">
<fragment
android:id="@+id/fragment_red"
android:name="com.playground.FragmentRed">
<argument
android:name="foo"
android:defaultValue="0"
app:argType="integer" />
<action
android:id="@+id/action_fragment_red_to_fragment_green"
app:destination="@id/fragment_green" />
</fragment>
<navigation
android:id="@+id/secondLevel"
app:startDestination="@id/fragment_green">
<fragment
android:id="@+id/fragment_green"
android:name="com.playground.FragmentGreen">
<argument
android:name="bar"
android:defaultValue="0"
app:argType="integer" />
<action
android:id="@+id/action_fragment_green_to_fragment_blue"
app:destination="@id/fragment_blue" />
</fragment>
<fragment
android:id="@+id/fragment_blue"
android:name="com.playground.FragmentBlue">
<argument
android:name="zar"
android:defaultValue="0"
app:argType="integer" />
</fragment>
</navigation>
</navigation>
现在在 MainActivity
让我们生成一个新通知,为每个片段传递参数:"foo"(红色)- 1,"bar"(绿色)- 2,"zar"(蓝色)- 3.
我们的期望是点击通知打开带有文本 3 的蓝屏,在返回点击后看到带有 2 的绿屏,再次点击应该在屏幕上显示带有 1 的红屏:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navController = findNavController(R.id.nav_host_fragment)
val pendingIntent = navController.createDeepLink()
.setGraph(R.navigation.main_graph)
.setDestination(R.id.fragment_blue)
.setArguments(bundleOf("foo" to 1, "bar" to 2, "zar" to 3))
.createPendingIntent()
createNotificationChannel() // outside of the scope of this answer
val builder = NotificationCompat.Builder(this, "my_channel")
.setContentTitle("title")
.setContentText("content text")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setSmallIcon(R.drawable.android)
.setAutoCancel(true)
.setChannelId("channelId")
with(NotificationManagerCompat.from(this)) {
notify(100, builder.build())
}
}
}
这是设备上的实际行为: