前言 Dialog 是端上非常常见的弹框。 使用起来跟 Activity 有点类似。 我们也尝尝会跟 popwindow 进行对比。 我们先来看看 Dialog 的代码分析一下。
基本使用 1 2 3 4 5 6 7 8 9 class MyDialog (context: Context) : Dialog(context) { override fun onCreate (savedInstanceState: Bundle ?) { super .onCreate(savedInstanceState) setContentView(0 ) } } MyDialog(this ).show()
定义和使用很简单,也是我们经常使用的, 我们就不再多说了。
源码分析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public Dialog(@NonNull Context context) { this (context, 0 , true ); } Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) { if (createContextThemeWrapper) { if (themeResId == ResourceId.ID_NULL) { final TypedValue outValue = new TypedValue(); context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true ); themeResId = outValue.resourceId; } mContext = new ContextThemeWrapper(context, themeResId); } else { mContext = context; } mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); final Window w = new PhoneWindow(mContext); mWindow = w; w.setCallback(this ); w.setOnWindowDismissedCallback(this ); w.setOnWindowSwipeDismissedCallback(() -> { if (mCancelable) { cancel(); } }); w.setWindowManager(mWindowManager, null , null ); w.setGravity(Gravity.CENTER); mListenersHandler = new ListenersHandler(this ); }
通过构造函数里可以看出来的几点。
我们可以设置当前弹框自己的主题。
Dialog 自己持有 wms。
Dialog 自己持有 window, 即 PhoneWindow。
其他的先不说, 通过 PhoneWndow 及设置我们是不是有种熟悉的关键, 对, 就在 Android 事件源头分析中有, 还基本一致哈, 所以我们应该关心的有几点。
setCallback PhoneWindow 把事件可以回调回来给 Dialog。
PhoneWindw 在设置布局的时候肯定最外层也是 DecorView。 所以肯定是有自己的主题呢。
所以我们看看 Dialog 的 setContentView 方法。
1 2 3 public void setContentView(@LayoutRes int layoutResID) { mWindow.setContentView(layoutResID); }
这不是跟 Activity 一毛一样嘛, mWindow 是 PhoneWindow,setContentView 内部根据主题构造 DecorView, 然后把我们的布局设置到 content 布局中。 根据 Android 事件源头的分析, Activity 是在 ActivityThread 中 handleResumeActivity 中 WMS addView 把 DectorView 添加进去的。 那么 Dialog 呢, 它是在 show 中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public void show() { if (mShowing) { if (mDecor != null ) { if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) { mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR); } mDecor.setVisibility(View.VISIBLE); } return ; } mCanceled = false ; if (!mCreated) { dispatchOnCreate(null ); } else { final Configuration config = mContext.getResources().getConfiguration(); mWindow.getDecorView().dispatchConfigurationChanged(config); } onStart(); mDecor = mWindow.getDecorView(); if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) { final ApplicationInfo info = mContext.getApplicationInfo(); mWindow.setDefaultIcon(info.icon); mWindow.setDefaultLogo(info.logo); mActionBar = new WindowDecorActionBar(this ); } WindowManager.LayoutParams l = mWindow.getAttributes(); boolean restoreSoftInputMode = false ; if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0 ) { l.softInputMode |= WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; restoreSoftInputMode = true ; } mWindowManager.addView(mDecor, l); if (restoreSoftInputMode) { l.softInputMode &= ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; } mShowing = true ; sendShowMessage(); }
show 里的代码没有删除任何代码, 因为简单所以直接全部复制了。 可以看到大致跟 Activity 中分析的基本一致, mWindowManager addView 了。 内部肯定也是 RootViewImpl 等去处理的。 这里就不再多分析了。 所以说 Dialog 的事件传递跟 Activity 的一致哈, 是单独的, 互不影响。 到这里我们就分析完成了。 很简单吧。 我们另外看看 Dialog 的生命周期吧。 在 shuw 方法中, 如果没有 mCreate 的话, 就执行 onCreate 方法。 所以说, 在没有调用 show 方法时, 是没有布局的话。 然后紧接着就会 onStart 生命周期的回调了。 那么有 onStart 肯定就有 onStop 对应吧, 不然就太鸡肋了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void dismissDialog() { if (mDecor == null || !mShowing) { return ; } if (mWindow.isDestroyed()) { Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!" ); return ; } try { mWindowManager.removeViewImmediate(mDecor); } finally { if (mActionMode != null ) { mActionMode.finish(); } mDecor = null ; mWindow.closeAllPanels(); onStop(); mShowing = false ; sendDismissMessage(); } }
看到了吧, 是在 dismiss dialog 时调用到啦。 看到 show 和 dismiss 我们可以发现一个点就是, 每次 show 的时候, 就会把布局添加进去, dismiss 都会把布局给移出。