结论

DialogFragment一个会自己弹出 Dialog 窗口的 Fragment

  • 对象身份是 Fragment,生命周期由 FragmentManager 驱动。
  • 画面画在独立的 android.app.Dialog 窗口里,不进入 Activity 的 FrameLayout
  • show() 只提交事务。真正 Dialog.show() 发生在 onStart
  • onCreate 定 theme,onCreateDialog 建窗口,onCreateView / onViewCreated 建内容,onActivityCreated 把 View 塞进 Dialog。

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

官方定义见 Display dialogs with DialogFragment:Dialog 的显示、隐藏、关闭由 Fragment API 驱动,不要直接操作 Dialog。

三个对象

日常说的「弹窗」经常把下面三件事混成一件:

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

身份写在继承上:

open class CardDialog : DialogFragment()

展示也走 Fragment API:

dialog.show(supportFragmentManager, null)

this 是 Fragment。dialog 字段才是窗口。requireView() 才是卡片那棵 View 树。

DialogFragment 同时扮演两个对象

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

内容挂在哪一个窗口

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

普通 Fragment 与 DialogFragment 的窗口归属

所以 onCreateViewcontainernull:内容稍后由框架放进 Dialog,不塞进 Activity 布局。inflate 时根节点没有 parent,XML 上的 layout_width / layout_height 不会立刻变成 layoutParams

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

生命周期

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

四个参与者:

角色职责
Activity调用 show(),提交事务
FragmentManager稍后执行事务,驱动回调
DialogFragment建 theme、窗口、内容树
DialogonStartshow() / 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 造内容。

每个回调改什么

回调按时间排成一条链。改动按对象落点:窗口属性走 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 生命周期看。


相关笔记