问题描述
在我的应用中,用户可以选择一个类别,然后在该类别中选择一个项目以最终查看该项目的详细信息.标准/转发流为:
在选择类别时,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)
值查询并显示所选类别中的适当项目.
那很好.但是我现在正在尝试在用户点击通知时实施深层链接,并使用退格将他们直接跳转到ViewItemDetailsFragment
,这会使他们先升到SelectItemFragment
,然后升到SelectCategoryFragment
.
我的问题是,如上所述,SelectItemFragment
依赖于传递给它的ARG_CATEGORY_ID
自变量来检索/显示其数据.我已经阅读了深度链接和嵌套导航图,但真的不知道如何通过ARG_CATEGORY_ID
深度链接/反向堆栈.
当用户按下时,是否可以将数据从ViewItemDetailsFragment
传递到SelectItemFragment
的整洁方式?
TLDR :可以使用嵌套图解决问题.有关更多详细信息,请参阅此文章.
更长的答案
让我们定义简单的片段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
.
我们这样声明主要活动布局:
<?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./p>
我们的期望是,单击通知以打开带有文本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())
}
}
}
这是设备上的实际行为:
In my app, the user can select a category, then select an item within that category to finally view the item details. The standard/forward flow is:
On selecting a category, the selectedCatId
is passed via a Bundle
from SelectCategoryFragment
to 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
will then use the getArguments().getLong(ARG_CATEGORY_ID)
value to query and display the appropriate items from the selected category.
That works fine. But I am now trying to implement deep linking when the users taps on a Notification, jumping them straight to ViewItemDetailsFragment
with a backstack that can take them up to SelectItemFragment
, then SelectCategoryFragment
.
My problem is that, as described, SelectItemFragment
depends on the ARG_CATEGORY_ID
argument being passed to it in order to retrieve/display its data. I've read up on deep linking and nested navigation graphs, but don't really know how to pass ARG_CATEGORY_ID
with deep linking/backstacks.
Is there a tidy way I can pass data from ViewItemDetailsFragment
to SelectItemFragment
when the user presses back?
TLDR: problem might be solved using nested graphs. Refer to this article for more details.
Longer answer
Let's define simple fragment FragmentRed
, FragmentGreen
and FragmentBlue
who inflate simple layouts each with corresponding background color:
<?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>
And declare fragment classes as such:
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
and FragmentBlue
are copy pasted, but substituted all color texts with corresponding color texts, i.e. FragmentRedArgs
-> FragmentBlueArgs
, R.layout.red
-> R.layout.blue
.
Let's declare main activity layout as such:
<?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>
Where main_graph
is:
<?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>
Now inside MainActivity
let's spawn a new notification, passing in arguments for each of fragments: "foo" (red) - 1, "bar" (green) - 2, "zar" (blue) - 3.
Our expectation is upon clicking on notification to open Blue screen with text 3, upon back click see Green screen with 2 and another click should bring Red screen with 1 on the screen:
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())
}
}
}
Here's the actual behavior on device:
这篇关于从通知进行的深层链接-如何通过后台将数据传递回去?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!