从通知进行的深层链接-如何通过后台将数据传递回去? [英] Deep linking from Notification - how to pass data back up through the backstack?

本文介绍了从通知进行的深层链接-如何通过后台将数据传递回去?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用中,用户可以选择一个类别,然后在该类别中选择一个项目以最终查看该项目的详细信息.标准/转发流为:

SelectCategoryFragment-> SelectItemFragment-> ViewItemDetailsFragment

在选择类别时,selectedCatId通过BundleSelectCategoryFragment传递到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 :可以使用嵌套图解决问题.有关更多详细信息,请参阅此文章.

更长的答案

让我们定义简单的片段FragmentRedFragmentGreenFragmentBlue,它们分别为简单的布局填充相应的背景色:

<?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
    }
}

FragmentGreenFragmentBlue是复制粘贴的,但是将所有颜色文本替换为相应的颜色文本,即FragmentRedArgs-> FragmentBlueArgsR.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:

SelectCategoryFragment -> SelectItemFragment -> ViewItemDetailsFragment

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:

这篇关于从通知进行的深层链接-如何通过后台将数据传递回去?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆