相当于 Android 架构导航的 startActivityForResult()

Equivalent of startActivityForResult() with Android Architecture Navigation

我有一个包含 3 个屏幕的工作流程。从"screen 1"到访问"screen 2",用户必须接受我在图片"modal"中调用的某种条款和条件。但他只需要接受这些条件一次。下次他在第一个屏幕时,他可以直接转到屏幕 2。用户可以选择不接受条款,因此我们返回 "screen 1" 并且不要尝试转到 "screen 2".

我想知道如何使用新的 navigation component

以前,我会怎么做:

但是有了navigation graph,就没办法启动一个Fragment去获取结果了

我可以在 "modal" 屏幕中将条款标记为已接受,然后从那里开始 "screen 2"。问题是要访问屏幕 2,我需要执行网络请求。我不想重复调用 API 并在 "screen 1" 和 "modal" 中处理其结果。

有没有办法使用 Jetpack 导航从 "modal" 返回 "screen 1" 并提供一些信息(用户接受了条款)?

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

目前导航组件中似乎没有 startActivityForResult 的等效项。但是,如果您使用的是 LiveData 和 ViewModel,您可能会对 this article 感兴趣。作者正在使用 activity 范围内的 ViewModel 和 LiveData 来为片段实现这一点。

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

  1. fun navigateBackWithResult(result: Bundle) 如此处解释 https://medium.com/google-developer-experts/using-navigation-architecture-component-in-a-large-banking-app-ac84936a42c2

  2. 创建回调。

ResultCallback.kt

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

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

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

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

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

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

要获取调用者片段,请使用类似 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
        }
    }
}

最近(在 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,并使用与 A

中的 LiveData 相同的键设置结果
findNavController().previousBackStackEntry?.savedStateHandle?.set("key", result)

Related documentation

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

Added support for passing results between two Fragments via new APIs on FragmentManager. This works for hierarchy fragments (parent/child), DialogFragments, and fragments in Navigation and ensures that results are only sent to your Fragment while it is at least STARTED. (b/149787344)

FragmentManager获得了两个新方法:

如何使用?

FragmentA中将FragmentResultListener添加到onCreate方法中的FragmentManager

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

FragmentB 中将此代码添加到 return 结果:

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

开始 FragmentB 例如:通过使用:

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

给close/finishFragmentB打电话:

findNavController().navigateUp()

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

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