Android jetpack 导航组件结果来自对话框

SMG*_*ost 14 android android-jetpack android-architecture-navigation

到目前为止,我只能使用导航组件成功导航到对话框并返回。问题是,我必须在对话框中做一些事情并将结果返回到调用对话框的片段。

一种方法是使用共享视图模型。但为此,我必须使用 .of(activity) ,即使我不再需要它,它也会让我的应用程序单例占用内存。

另一种方法是覆盖 show(fragmentManager, id) 方法,访问片段管理器,并从中访问前一个片段,然后可以将其设置为目标片段。我在实现回调接口之前使用过 targetFragment 方法,因此我的对话框可以将结果通知 targetFragment。但是在导航组件方法中,它感觉很笨拙,可能会在某一时刻停止工作。

还有其他方法可以做我想做的事吗?也许有办法解决第一种方法的问题?

Ham*_*ari 12

在 Navigation 2.3.0-alpha02 及更高版本中,NavBackStackEntry 允许访问 SavedStateHandle。SavedStateHandle 是可用于存储和检索数据的键值映射。这些值在进程死亡期间持续存在,包括配置更改,并通过同一对象保持可用。通过使用给定的 SavedStateHandle,您可以在目标之间访问和传递数据。这作为一种在数据从堆栈中弹出后从目的地取回数据的机制特别有用。

要将数据从 Destination B 传回 Destination A,首先设置 Destination A 以侦听其 SavedStateHandle 上的结果。为此,请使用 getCurrentBackStackEntry() API 检索 NavBackStackEntry,然后观察 SavedStateHandle 提供的 LiveData。

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val navController = findNavController();
// We use a String here, but any type that can be put in a Bundle is supported
navController.currentBackStackEntry?.savedStateHandle?.getLiveData("key")?.observe(
    viewLifecycleOwner) { result ->
    // Do something with the result.
}
Run Code Online (Sandbox Code Playgroud)

}

在目标 B 中,您必须使用 getPreviousBackStackEntry() API 在目标 A 的 SavedStateHandle 上设置结果。

navController.previousBackStackEntry?.savedStateHandle?.set("key", result)
Run Code Online (Sandbox Code Playgroud)


dum*_*ers 10

感谢@NataTse 和官方文档,我想出了扩展,以便希望编写更少的样板代码:

fun <T>Fragment.setNavigationResult(key: String, value: T) {
    findNavController().previousBackStackEntry?.savedStateHandle?.set(
        key,
        value
    )
}

fun <T>Fragment.getNavigationResult(@IdRes id: Int, key: String, onResult: (result: T) -> Unit) {
    val navBackStackEntry = findNavController().getBackStackEntry(id)

    val observer = LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_RESUME
            && navBackStackEntry.savedStateHandle.contains(key)
        ) {
            val result = navBackStackEntry.savedStateHandle.get<T>(key)
            result?.let(onResult)
            navBackStackEntry.savedStateHandle.remove<T>(key)
        }
    }
    navBackStackEntry.lifecycle.addObserver(observer)

    viewLifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_DESTROY) {
            navBackStackEntry.lifecycle.removeObserver(observer)
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

  • 这是我发现的处理此问题的最佳扩展函数。具体包括`navBackStackEntry.savedStateHandle.remove&lt;T&gt;(key)`。救星! (3认同)

小智 5

当你在对话框中使用导航组件时,这部分代码看起来不太好(对我来说它什么都不返回)

navController.currentBackStackEntry?.savedStateHandle?.getLiveData("key")?.observe(
viewLifecycleOwner) { result ->
// Do something with the result.}
Run Code Online (Sandbox Code Playgroud)

你需要从官方文档中尝试方法,它对我有很大帮助

这部分对我有用:

 val navBackStackEntry = navController.getBackStackEntry(R.id.target_fragment_id)

    // Create observer and add it to the NavBackStackEntry's lifecycle
    val observer = LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_RESUME
            && navBackStackEntry.savedStateHandle.contains("key")
        ) {
            val result =
                navBackStackEntry.savedStateHandle.get<Boolean>("key")
            // Do something with the result

        }
    }
    navBackStackEntry.lifecycle.addObserver(observer)

    // As addObserver() does not automatically remove the observer, we
    // call removeObserver() manually when the view lifecycle is destroyed
    viewLifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_DESTROY) {
            navBackStackEntry.lifecycle.removeObserver(observer)
        }
    })
Run Code Online (Sandbox Code Playgroud)

在你的对话中:

navController.previousBackStackEntry?.savedStateHandle?.set(
            "key",
            true
        )
Run Code Online (Sandbox Code Playgroud)

  • 更新:ShareViewModel 效果不佳。所以我再次尝试了你的方法,并确保“target_fragment_id”是源片段而不是目标片段的ID,我可以确认,它有效。谢谢,这真的很有帮助。 (2认同)