简而言之 - 使用这个AppDialog类既可以将数据传递到DialogFragment中,也可以从中获取结果。
详细解释:
- 前提 - 片段在配置更改时被销毁并重新创建。视图模型会一直存在。当使用对话框时,建议将其包装在DialogFragment中,以便在用户旋转设备并更改方向时,对话框不会意外消失(DialogFragment将重新创建它并重新显示它)。
- 限制(因此出现了这个问题)- DialogFragment的工作方式是它需要在配置更改时重新实例化一个类 - 这意味着不能有构造函数参数传递给子类,通常需要通过视图模型进行自定义回调来传递对话框结果。这通常意味着为每个对话框创建一个新的子类。
- 解决方案 - 为了帮助解决所有这些问题,这个自定义AppDialog片段应运而生 - 参数存储在内存中(类似于视图模型,您可以将其视为在内存中保存T的小型自定义视图模型,并使用它在配置更改时重新创建对话框),直到对话框片段被关闭。正确的回调方式是通过视图模型。如果显示AppDialog的片段,则可能已经有一个视图模型,您可以从用于创建对话框的lambda中引用它 - 这意味着在对话框片段被关闭之前,视图模型有额外的强引用。
- 示例 - 请参见示例,其中将简单对话框重构为使用此AppDialog实用程序类来接收参数并执行回调以通知viewModel结果。
辅助类:
class AppDialog<T>: DialogFragment() {
companion object {
fun<T> buildDialog(params: T? = null, builder: AppDialogLambda<T>): AppDialog<T> {
val args = Bundle()
args.putInt("key", pushDialogArgs(params, builder))
val fragment = AppDialog<T>()
fragment.arguments = args
return fragment
}
private var lastKey: Int = 0
private val dialogArgs = mutableMapOf<Int, Pair<Any?, AppDialogLambda<*>>>()
private fun pushDialogArgs(params: Any?, builder: AppDialogLambda<*>): Int {
dialogArgs[lastKey] = params to builder
return lastKey++
}
private fun getDialogArgs(key: Int): Pair<Any?, AppDialogLambda<*>> {
return dialogArgs[key]!!
}
private fun deleteDialogArgs(key: Int) {
dialogArgs.remove(key)
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val argKey = requireArguments().getInt("key")
val (params, builder) = getDialogArgs(argKey)
@Suppress("UNCHECKED_CAST")
return (builder as AppDialogLambda<T>)(this, params as T?)
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
val argKey = requireArguments().getInt("key")
deleteDialogArgs(argKey)
}
}
使用示例(之后):
val info = mapOf("message" to "${error.description}\n\nPlease check your Internet connection and try again.")
AppDialog.buildDialog(info) { fragment, params ->
fragment.isCancelable = false
AlertDialog.Builder(fragment.context)
.setTitle("Terms Of Service Failed To Load")
.setMessage(params!!["message"])
.setPositiveButton("Retry") { _, _ ->
viewModel.onTermsOfServiceReload()
}
.setNegativeButton("Cancel") { _, _ ->
viewModel.onTermsOfServiceDeclined()
fragment.findNavController().popBackStack()
}.create()
}.show(parentFragmentManager, "TOS Failed Dialog")
使用示例(之前):
不使用DialogFragment(仅用于说明目的,不要这样做,因为对话框将在配置更改时被销毁),在UserTOSFragment.kt中的代码 - 请注意,代码直接调用UserTOSFragment.loadContent()以进行重试。必须将其重写为在上面的示例中调用viewModel.onTermsOfServiceDeclined():
AlertDialog.Builder(context)
.setTitle("Terms Of Service Failed To Load")
.setMessage("${error.description}\n\nPlease check your Internet connection and try again.")
.setPositiveButton("Retry") { _, _ ->
loadContent()
}
.setCancelable(false)
.setNegativeButton("Cancel") { _, _ ->
viewModel.onTermsOfServiceDeclined()
findNavController().popBackStack()
}
.show()
Activity
和DialogFragment
都可能被重新创建。使用传递给onAttach(Activity activity)
的Activity
是正确和推荐的方式。 - sstn