56

我有一个带有 3 个屏幕的工作流程。从“屏幕 1”到访问“屏幕 2”,用户必须接受我在图片中称为“模态”的某种条款和条件。但他只需要接受一次这些条件。下次他在第一个屏幕时,他可以直接进入屏幕 2。用户可以选择不接受条款,因此我们返回“屏幕 1”,不要尝试进入“屏幕 2”。

应用工作流程

我想知道如何使用新的导航组件来做到这一点。

以前,我会做什么:

  • 在屏幕 1 上,检查用户是否必须接受条件
  • 如果否,则启动“屏幕 2”活动
  • 如果是,则使用startActivityForResult()并等待模态的结果。将条款标记为已接受。启动“屏幕 2”

但是有了导航图,就没有办法启动一个Fragment来获取结果。

我可以在“模态”屏幕中将条款标记为已接受,然后从那里启动“屏幕 2”。问题是要访问屏幕 2,我需要进行网络请求。我不想复制对 API 的调用并在“屏幕 1”和“模态”中处理其结果。

有没有办法使用 Jetpack 导航从“模态”返回到“屏幕 1”并提供一些信息(用户接受条款)?

编辑:我目前通过使用 Yahya 在下面建议的相同流程来解决它:仅将 Activity 用于模态并startActivityForResult从“屏幕 1”使用。我只是想知道我是否可以继续在整个流程中使用导航图。

4

6 回答 6

20

共享视图模型有几种替代方案。

  1. 有趣的 navigateBackWithResult(result: Bundle) 如此处所述https://medium.com/google-developer-experts/using-navigation-architecture-component-in-a-large-banking-app-ac84936a42c2

  2. 创建回调。

结果回调.kt

interface ResultCallback : Serializable {
    fun setResult(result: Result)
}

将此回调作为参数传递(注意它必须实现 Serializable 并且接口需要在自己的文件中声明。)

<argument android:name="callback"
                  app:argType="com.yourpackage.to.ResultCallback"/>

让framgent A 实现ResultCallback,fragment B by 将获取参数并通过它们传回数据,args.callback.setResult(x)

于 2019-04-16T12:21:33.387 回答
20

在 AndroidX Fragment 库的1.3.0-alpha04版本中,他们引入了允许在Fragments 之间传递数据的新 API。

添加了对通过 FragmentManager 上的新 API 在两个 Fragment 之间传递结果的支持。这适用于导航中的层次结构片段(父/子)、DialogFragments 和片段,并确保仅在至少开始时将结果发送到您的片段。( b/149787344 )

FragmentManager获得了两种新方法:

如何使用它?

在方法中FragmentA添加:FragmentResultListenerFragmentManageronCreate

setFragmentResultListener("request_key") { requestKey: String, bundle: Bundle ->
    val result = bundle.getString("your_data_key")
    // do something with the result
}

添加此代码以FragmentB返回结果:

val result = Bundle().apply {
    putString("your_data_key", "Hello!")
}
setFragmentResult("request_key", result)

开始FragmentB例如:通过使用:

findNavController().navigate(NavGraphDirections.yourActionToFragmentB())

关闭/结束FragmentB通话:

findNavController().navigateUp()

现在,您FragmentResultListener应该会收到通知,并且您应该会收到结果。

(我fragment-ktx用来简化上面的代码)

于 2020-05-19T13:07:57.267 回答
18

看起来startActivityForResult现在导航组件中没有等效的 for 。但如果您使用 LiveData 和 ViewModel,您可能会对本文感兴趣。作者正在使用活动范围的 ViewModel 和 LiveData 来为片段实现这一点。

于 2018-08-08T09:07:15.250 回答
16

最近(在 androidx-navigation-2.3.0-alpha02 中)Google 发布了一种正确的方法来实现片段的这种行为。

简而言之:(来自发行说明)

如果Fragment A需要来自Fragment B..的结果

A应该从currentBackStackEntry获取 savedStateHandle ,调用 getLiveData 提供一个键并观察结果。

findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<Type>("key")?.observe(
    viewLifecycleOwner) {result ->
    // Do something with the result.
}

B应该从previousBackStackEntry获取 savedStateHandle ,并使用与 LiveData 相同的键设置结果A

findNavController().previousBackStackEntry?.savedStateHandle?.set("key", result)

相关文档

于 2020-02-19T01:48:47.013 回答
1

还有另一种解决方法。您可以使用从模态返回到 screen1 的另一个导航操作,而不是使用popBackStack(). 在该操作中,您可以发送您喜欢的任何数据来筛选一个。使用此策略确保模态屏幕不会保留在导航返回堆栈中:https ://stackoverflow.com/a/54015319/4672107 。

我看到这个策略的唯一问题是按下后退按钮不会发回任何数据,但是大多数用例需要在特定用户操作后导航,在这些情况下,这种解决方法将起作用。

于 2019-05-22T01:46:36.237 回答
0

要获取调用者片段,请使用类似的东西fragmentManager.putFragment(args, TargetFragment.EXTRA_CALLER, this),然后在目标片段中使用获取调用者片段

if (args.containsKey(EXTRA_CALLER)) {
    caller = fragmentManager?.getFragment(args, EXTRA_CALLER)
    if (caller != null) {
        if (caller is ResultCallback) {
            this.callback = caller
        }
    }
}
于 2019-11-24T14:57:26.627 回答