在Android创建窗口(一)创建应用窗口中介绍了应用窗口的创建过程,本文将介绍一下Dialog的创建过程。创建Dialog和创建应用窗口的过程有很多类似之处,因为前一篇文件介绍了,这里相似的地方就跳过了。
首先看看如何显示一个Dialog:
Dialog dialog=new Dialog(MainActivity.this);
dialog.setContentView(R.layout.dialog);
dialog.show();
显示Dialog的代码很简单,下面我们来一步一步的分析一下。
1.创建应用窗口,首先是创建一个Activty对象,那么创建Dialog肯定也是创建一个Dialog对象了,我们先去看看Dialog的构造函数:
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
...
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Window w = new PhoneWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
果然和应用窗口一样,先创建了一个Window对象。
2.接下来是设置contentview:
public void setContentView(@LayoutRes int layoutResID) {
mWindow.setContentView(layoutResID);
}
Dialog把这个任务交给了window来完成,和Activity类似,都是通过window来添加布局文件的。PhoneWindow的setContentView方法:
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
在Android创建窗口(一)创建应用窗口中介绍了这一方法,这里就不多说了。这一步就是把布局文件添加到decorview上。
3.将DecorView添加到Window上并显示出来
这一步是在Dialog的show方法中完成的,来看看源码
/**
* Start the dialog and display it on screen. The window is placed in the
* application layer and opaque. Note that you should not override this
* method to do initialization when the dialog is shown, instead implement
* that in {@link #onStart}.
*/
public void show() {
if (mShowing) {
if (mDecor != null) {//如果decorview已经存在就把它显示出来,
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 {
// Fill the DecorView in on any configuration changes that
// may have occured while it was removed from the WindowManager.
final Configuration config = mContext.getResources().getConfiguration();
mWindow.getDecorView().dispatchConfigurationChanged(config);
}
onStart();
//对decorview进行初始化操作
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();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
nl.copyFrom(l);
nl.softInputMode |=
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
l = nl;
}
//把decorView添加到window上
mWindowManager.addView(mDecor, l);
mShowing = true;
sendShowMessage();
}
show方法首先判断mDecor是否存为null,如果不为null就显示出来,如果为null,就对它初始化,并把decorview添加到window上。
以上三个步骤可以发现,创建Dialog和创建Activity的过程有很多类似之处.
我们再看看Dialog的dismiss
/**
* Dismiss this dialog, removing it from the screen. This method can be
* invoked safely from any thread. Note that you should not override this
* method to do cleanup when the dialog is dismissed, instead implement
* that in {@link #onStop}.
*/
@Override
public void dismiss() {
if (Looper.myLooper() == mHandler.getLooper()) {
dismissDialog();
} else {
mHandler.post(mDismissAction);
}
}
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();
}
}
这个比较简单,Dialog关闭时,会通过WindowManager把decorview从window上移除。
通过源码分析Android窗口的创建:
Android创建窗口(一)创建应用窗口
Android创建窗口(二)创建Dialog
Android 创建窗口(三) 创建Toast