Android Jetpack 导航组件来自对话框
Android jetpack navigation component result from dialog
到目前为止,我仅使用导航组件就可以成功导航到对话框并返回。问题是,我必须在对话框中做一些事情,return 导致调用对话框的片段。
一种方法是使用共享视图模型。但是为此,我必须使用 .of(activity) ,这使我的应用程序只有一个占用内存的单例,即使我不再需要它也是如此。
另一种方法是重写 show(fragmentManager, id) 方法,获取对片段管理器的访问权限,并从中访问前一个片段,然后可以将其设置为目标片段。我在实现回调接口之前使用过 targetFragment 方法,因此我的对话框可以将结果通知 targetFragment。但在导航组件方法中,它感觉很糟糕,可能会在某一时刻停止工作。
还有其他方法可以做我想做的事吗?也许有办法解决第一种方法的问题?
在 Navigation 2.3.0-alpha02 及更高版本中,NavBackStackEntry 提供对 SavedStateHandle 的访问权限。 SavedStateHandle 是可用于存储和检索数据的键值映射。这些值在进程死亡(包括配置更改)后仍然存在,并通过同一对象保持可用。通过使用给定的 SavedStateHandle,您可以在目标之间访问和传递数据。这作为一种在数据从堆栈中弹出后从目的地取回数据的机制特别有用。
要将数据从目标 B 传回目标 A,请首先设置目标 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.
}
}
在目标 B 中,您必须使用 getPreviousBackStackEntry() 在目标 A 的 SavedStateHandle 上设置结果 API。
navController.previousBackStackEntry?.savedStateHandle?.set("key", result)
当您将导航组件与对话框一起使用时,这部分代码看起来不太好(对我来说它没有返回任何内容)
navController.currentBackStackEntry?.savedStateHandle?.getLiveData("key")?.observe(
viewLifecycleOwner) { result ->
// Do something with the result.}
你需要尝试 official docs 的方法,这对我帮助很大
这部分对我有用:
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)
}
})
在你的对话中:
navController.previousBackStackEntry?.savedStateHandle?.set(
"key",
true
)
感谢@NataTse 和 official docs,我提出了扩展,希望可以减少编写的样板代码:
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)
}
})
}
到目前为止,我仅使用导航组件就可以成功导航到对话框并返回。问题是,我必须在对话框中做一些事情,return 导致调用对话框的片段。
一种方法是使用共享视图模型。但是为此,我必须使用 .of(activity) ,这使我的应用程序只有一个占用内存的单例,即使我不再需要它也是如此。
另一种方法是重写 show(fragmentManager, id) 方法,获取对片段管理器的访问权限,并从中访问前一个片段,然后可以将其设置为目标片段。我在实现回调接口之前使用过 targetFragment 方法,因此我的对话框可以将结果通知 targetFragment。但在导航组件方法中,它感觉很糟糕,可能会在某一时刻停止工作。
还有其他方法可以做我想做的事吗?也许有办法解决第一种方法的问题?
在 Navigation 2.3.0-alpha02 及更高版本中,NavBackStackEntry 提供对 SavedStateHandle 的访问权限。 SavedStateHandle 是可用于存储和检索数据的键值映射。这些值在进程死亡(包括配置更改)后仍然存在,并通过同一对象保持可用。通过使用给定的 SavedStateHandle,您可以在目标之间访问和传递数据。这作为一种在数据从堆栈中弹出后从目的地取回数据的机制特别有用。
要将数据从目标 B 传回目标 A,请首先设置目标 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.
}
}
在目标 B 中,您必须使用 getPreviousBackStackEntry() 在目标 A 的 SavedStateHandle 上设置结果 API。
navController.previousBackStackEntry?.savedStateHandle?.set("key", result)
当您将导航组件与对话框一起使用时,这部分代码看起来不太好(对我来说它没有返回任何内容)
navController.currentBackStackEntry?.savedStateHandle?.getLiveData("key")?.observe(
viewLifecycleOwner) { result ->
// Do something with the result.}
你需要尝试 official docs 的方法,这对我帮助很大
这部分对我有用:
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)
}
})
在你的对话中:
navController.previousBackStackEntry?.savedStateHandle?.set(
"key",
true
)
感谢@NataTse 和 official docs,我提出了扩展,希望可以减少编写的样板代码:
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)
}
})
}