MarkZ

三个对象

业务里写成这样:

open class CardDialog : DialogFragment()
dialog.show(supportFragmentManager, null)

日常把这两行叫「弹窗」。打开类看到的是 Fragment 子类,show() 交给的是 FragmentManager,屏幕上那张卡片却不在 Activity 的布局里。同一句话里叠了三个对象:

对象它是什么谁管它
Fragment一段有生命周期的 UI 逻辑FragmentManager
Dialog一个独立 WindowWindowManager
DialogFragment用 Fragment 生命周期去管一个 Dialog两者都管

this 是 Fragment。dialog 字段才是窗口。requireView() 才是卡片那棵 View 树。官方把显示、隐藏、关闭都交给 Fragment API,不要直接操作 Dialog,见 Display dialogs with DialogFragment。

DialogFragment 同时扮演两个对象

左边是宿主关系:Fragment 已经 onAttach 到 Activity。右边是窗口关系:View 树要等到 setContentView 才成为 Dialog 内容区的 child。根 View 缺的是右边那次 add,不是左边的 onAttach。

依据是 androidx.fragment:fragment:1.2.5 与平台 Dialog / LayoutInflater。较新 AndroidX 可能把 setContentView 从 onActivityCreated 挪走,但这条先后关系不变。

内容挂在哪一个窗口

普通 Fragment 的 View 挂在 Activity 窗口的 container 里。DialogFragment 的 View 挂在另一扇 Dialog 窗口里。

普通 Fragment 与 DialogFragment 的窗口归属

所以 onCreateView 的 container 是 null:内容稍后由框架放进 Dialog,不塞进 Activity 布局。inflate 时根节点没有 parent,XML 上的 layout_width / layout_height 不会立刻变成 layoutParams。在这里读根节点尺寸、或按 layoutParams 改窗口,对不上预期。

这也是为什么不用纯 new Dialog(context):Dialog 不管配置变化,没有 FragmentManager 恢复,生命周期要自己对齐。DialogFragment 用 Fragment 把窗口绑到宿主 Activity 上。代价是同时出现两套回调。

生命周期

show(fm, tag) 默认 commit() 入队。返回时窗口还不存在。生命周期由 FragmentManager 稍后 execPendingActions() 驱动。

四个参与者:

角色职责
Activity调用 show(),提交事务
FragmentManager稍后执行事务,驱动回调
DialogFragment建 theme、窗口、内容树
Dialog在 onStart 里 show() / addView
DialogFragment.show 之后的调用时序

DialogFragment 1.2.5 的调用点:

  1. onGetLayoutInflater() 里调用 onCreateDialog(),再 setupDialog()。官方注释:该方法在 onCreate 之后、onCreateView 之前。
  2. onCreateView() 正常由 Fragment 驱动,container 为空。
  3. onViewCreated() 在 onCreateView 返回之后。
  4. onActivityCreated() 里 mDialog.setContentView(view)。
  5. onStart() 里 mDialog.show(),内部走到 WindowManager.addView()。

onCreateDialog 必须早于 onCreateView:框架要先有带 theme 的 Dialog Context,再用它的 LayoutInflater 造内容。show() 只提交事务,真正让窗口对用户可见的是最后一步 Dialog.show()。

每个回调改什么

回调按时间排成一条链。改动按对象落点:窗口属性走 Dialog 回调,卡片内容走 Fragment 视图回调。

DialogFragment 生命周期
回调改什么此时有没有 View
onCreatesetStyle(),定 Dialog theme没有
onCreateDialognew Dialog,gravity、进场动画、是否无标题没有
onCreateViewinflate 布局正在创建
onViewCreatedfindView、绑文案和按钮有 View,根节点未必有 parent
onActivityCreated框架 setContentView,业务尽量不覆写根 View 挂到 Dialog
onStart窗口宽高、dim、系统蒙层已 setContentView,即将 Dialog.show()

setStyle() 必须放在 onCreate。官方要求:Dialog 创建后再调没有效果。

onActivityCreated 是 这个 Fragment 的回调,不是「宿主 Activity 刚创建完」。Activity 可能早已 onResume;弹窗是后来 show() 的,这个 Fragment 的 onActivityCreated 也是后跑的。AndroidX 已把它标成 deprecated,业务改用 onViewCreated / onCreate。1.2.5 仍在这里 setContentView,读框架源码时还得认这个名字。

改动落点

先判断改的是窗口还是内容:

要改的东西放哪
theme、点击外部是否关闭onCreate + setStyle
gravity、进场动画onCreateDialog
窗口宽高、dim、系统蒙层onStart
inflate 哪份 XMLonCreateView
文案、按钮、ViewStubonViewCreated

同一页面上的浮层不一定都是 DialogFragment:

形态载体
居中模态卡片DialogFragment
贴在当前窗口的半屏面板PopupWindow
轻提示Toast
独立一页的 Dialog 外观Dialog 主题 Activity

打开类时按继承判断:: DialogFragment() 走上面这套回调;PopupWindow 没有 onCreateView;Dialog 主题 Activity 按 Activity 生命周期看。配置变化和异步展示的竞态见 BadTokenException:Dialog异步显示与Activity生命周期竞态。


相关笔记