7.1 对话框AlertDialog与DialogFragment


7.1 对话框 AlertDialog 与 DialogFragment

本节摘要:AlertDialog 提供模态决策的界面构件,DialogFragment 给它安上生命周期与重建恢复的家。本节完成"放弃对局确认"与"帮助说明"两个对话框,讲清 Builder 写法、旋转消失事故的成因与 Fragment 托管的正规姿势。

三分钟版:Builder 搭一个确认框

5.2 节拦下了返回键,现在补上 showGiveUpDialog 的实现。AlertDialog 用 Builder 链式搭建,标题、正文、确认、取消四件套:

AlertDialog.Builder(this) .setTitle("放弃本局?") .setMessage("当前进度将不会被保存") .setPositiveButton("放弃") { _, _ -> finish() } .setNegativeButton("继续猜") { _, _ -> } .show()

Builder 模式值得多说一句:AlertDialog 需要的零件有七八样、大多可选,用构造函数得准备一长串重载,Builder 让每样零件独立setter、链式拼装——你在 4.3 节的 ConstraintLayout 属性写法、第 6 章的 Snackbar 链式调用里都在用同一个思想。语义约定也顺带记住:破坏性动作放 positive 按钮时要三思,取消路径必须存在且不必思考。"继续猜"的监听器空实现不是偷懒,是显式声明"点了就关框,无事发生"。

帮助说明框换一种搭法,用列表承载内容:

val steps = arrayOf("输入 1 到 100 的整数", "根据提示调整方向", "七次内猜中即胜利") AlertDialog.Builder(this) .setTitle("玩法说明") .setItems(steps) { _, which -> Toast.makeText(this, steps[which], Toast.LENGTH_SHORT).show() } .show()

旋转消失事故

上面两个框在演示时都好好的,直到你转了一下模拟器:对话框凭空消失,用户的选择被迫中断。成因藏在 Activity 的生命周期里——屏幕旋转触发 Activity 销毁重建(第 1 章提过的那扇旋转之门),直接 show 出来的 AlertDialog 挂在旧 Activity 窗口上,随旧窗口一起被回收;新 Activity 对它一无所知。比消失更险的变体是持有 Context 的监听器没被释放,对着已死的 Activity 调 finish 崩给你看。

解药是 DialogFragment——把对话框装进一个有自己生命周期的 Fragment,由 FragmentManager 托管。重建时 FragmentManager 恢复这个 Fragment,对话框随之复现:

class GiveUpDialog : DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return AlertDialog.Builder(requireContext()) .setTitle("放弃本局?") .setMessage("当前进度将不会被保存") .setPositiveButton("放弃") { _, _ -> (activity as? MainActivity)?.reallyGiveUp() } .setNegativeButton("继续猜") { _, _ -> } .create() } }

调用方只剩一行,注意用 childFragmentManager 或 getSupportFragmentManager 而非直接 show:

GiveUpDialog().show(supportFragmentManager, "give_up")

两个细节值得圈点。其一,activity as? MainActivity 是对话框回传结果的朴素写法——安全转换加可空链,比强转优雅;正式项目可用 Fragment Result API 或共享 ViewModel,思路相同:对话框只管问,决定权交回宿主。其二,onCreateDialog 里用 requireContext() 拿上下文,它保证 Fragment 已附着,比缓存的 activity 引用安全。

图:两条生命周期路线的对照

图:两条生命周期路线的对照

本节要点回顾

  • Builder 链式拼装:标题正文按钮独立设置, Snackbar ConstraintLayout 同思想。
  • 旋转是照妖镜:裸 AlertDialog 随旧窗口消失,DialogFragment 由 FragmentManager 托管恢复。
  • requireContext 安全拿上下文:比缓存 activity 引用稳。
  • 对话框只管问:决定通过安全转换或 Result API 交回宿主执行。
  • 取消不必思考:negative 按钮空实现是显式语义,不是偷懒。

两个延伸追问

全屏页面也能用 DialogFragment 吗? 能,把样式配成全屏主题即可,Fragment 的展示形态本来就是可配的。反过来讲,DialogFragment 的本质是"有对话框皮肤的 Fragment"——理解了这句话,它还能当底部抽屉、内嵌弹层用,一个容器多种皮肤。

底部弹出的 BottomSheetDialog 和它什么关系? 同族工具,同由 fragment 托管体系支撑,皮肤不同、生命周期协议一致。选型看内容形态:居中决策用 AlertDialog、底部列表与表单用 BottomSheet,方向感比喜好更值得作为依据。

顺带交代对话框与 4.4 节 FrameLayout 的呼应:Dialog 之所以能有独立窗口,靠的正是系统在窗口层为其提供的容器,而 Fragment 的展示也常依托 Activity 布局里预留的容器 id——两条知识线在这里接上了头,这也是复习第 4 章"占位"语义的好时机。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U