相当于 Android 架构导航的 startActivityForResult()
Equivalent of startActivityForResult() with Android Architecture Navigation
我有一个包含 3 个屏幕的工作流程。从"screen 1"到访问"screen 2",用户必须接受我在图片"modal"中调用的某种条款和条件。但他只需要接受这些条件一次。下次他在第一个屏幕时,他可以直接转到屏幕 2。用户可以选择不接受条款,因此我们返回 "screen 1" 并且不要尝试转到 "screen 2".
我想知道如何使用新的 navigation component。
以前,我会怎么做:
- 在屏幕 1 上,检查用户是否必须接受条件
- 如果没有,开始"screen 2"activity
- 如果是,使用
startActivityForResult()
并等待模态的结果。将条款标记为已接受。开始 "screen 2"
但是有了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 来为片段实现这一点。
共享视图模型有几个替代方案。
fun navigateBackWithResult(result: Bundle) 如此处解释 https://medium.com/google-developer-experts/using-navigation-architecture-component-in-a-large-banking-app-ac84936a42c2
创建回调。
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)
在 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
获得了两个新方法:
FragmentManager#setFragmentResult(String, Bundle)
which you can treat similiarly to the existing Activity#setResult
;
FragmentManager#setFragmentResultListener(String, LifecycleOwner, FragmentResultListener)
允许您 listen/observe 结果更改。
如何使用?
在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
来简化上面的代码)
我有一个包含 3 个屏幕的工作流程。从"screen 1"到访问"screen 2",用户必须接受我在图片"modal"中调用的某种条款和条件。但他只需要接受这些条件一次。下次他在第一个屏幕时,他可以直接转到屏幕 2。用户可以选择不接受条款,因此我们返回 "screen 1" 并且不要尝试转到 "screen 2".
我想知道如何使用新的 navigation component。
以前,我会怎么做:
- 在屏幕 1 上,检查用户是否必须接受条件
- 如果没有,开始"screen 2"activity
- 如果是,使用
startActivityForResult()
并等待模态的结果。将条款标记为已接受。开始 "screen 2"
但是有了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 来为片段实现这一点。
共享视图模型有几个替代方案。
fun navigateBackWithResult(result: Bundle) 如此处解释 https://medium.com/google-developer-experts/using-navigation-architecture-component-in-a-large-banking-app-ac84936a42c2
创建回调。
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
findNavController().previousBackStackEntry?.savedStateHandle?.set("key", result)
在 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
获得了两个新方法:
FragmentManager#setFragmentResult(String, Bundle)
which you can treat similiarly to the existingActivity#setResult
;FragmentManager#setFragmentResultListener(String, LifecycleOwner, FragmentResultListener)
允许您 listen/observe 结果更改。
如何使用?
在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
来简化上面的代码)