上一篇博客我们介绍了Activity的Window创建过程及最后如何展示出来的,这一篇我们接着分析Dialog的Window创建及展示。
Dialog在我们的开发中也算是一个特别常用的组件了, 比如AlertDialog, ProgressDialog、自定义Dialog等等, 他们都是Dialog类的子类, 所以这里我们就直接分析Dialog了。
#####一、创建一个简单的dialog
上一篇博客我们介绍了Activity的Window创建过程及最后如何展示出来的,这一篇我们接着将Dialog的Window创建及展示。先来看一个简单的dialog的例子, 代码如下:
private void dialogTest() {
Dialog dialog = new Dialog(this);
//创建dialog的内容为一个LinearLayout, 内部包含了一个textview和Button
LinearLayout ll = new LinearLayout(this);
ll.setGravity(Gravity.CENTER);
ll.setPadding(100, 0, 100, 0);
TextView title = new TextView(this);
title.setText("标题");
Button bt_confirm = new Button(this);
bt_confirm.setText("确定");
bt_confirm.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG, "点击确定");
}
});
ll.setOrientation(LinearLayout.VERTICAL);
ll.addView(title);
ll.addView(bt_confirm);
//给dialog设置布局文件
dialog.setContentView(ll);
dialog.show();
}
这样, 我们就创建出了一个dialog, 运行效果如下:
下面, 我们就进入Dialog的身体, 去看看它的源码吧~
#####一、创建Window
从构造函数看起, 首先初始化一个Dialog:
Dialog(Context context, int themeResId, boolean createContextThemeWrapper) {
...
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Window w = PolicyManager.makeNewWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
这里的Window依然是通过PolicyManager.makeNewWindow()创建的。这个过程和上一篇博客Activity的Window创建过程类似。 版本较高的代码, 这里可能看到的代码是这样的:
final Window w = new PhoneWindow(mContext);
mWindow = w;
细节虽然不同, 但是整体流程都是相同的
#####二、初始化DecorView并将Dialog的布局视图添加到DecorView中
在上面的示例代码里, 我们通过这一行代码,给dialog填充了布局:
//给dialog设置布局文件
dialog.setContentView(ll);
进入源码看一些这个setContentView方法:
public void setContentView(View view) {
mWindow.setContentView(view);
}
可以看到, 这里通过我们第一步创建的Window来添加布局的, 上一章已经讲过了Window的唯一实现类是PhoneWindow,所以我们可以到PhoneWindow里去看看setContentView这个方法的源码。 其实从这里开始, 就和我们上一章分析Activity的Window添加的过程相似了,我们还是简单说下把。接着看 PhoneWindow的etContentView方法:
@Override
public void setContentView(View view) {
setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
...
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
...
} else {
mContentParent.addView(view, params);
}
...
}
setContentView中有一些判断, 我们忽略掉, 毕竟PhoneWindow是公用代码,别的View来了也走这里, 判断比较多。其实从上面的示例代码也很简单, 我们并没有设置那么多参数啊flag之类的,所以 我们只看重点代码, 这里我们最终会调用 mContentParent.addView(view, params)这个方法, 这里的mContentParent 其实就是我们的Activity的 布局文件的View对象,这在上一篇博客已经分析过了, 所以这个Dialog的view 最终添加到了我们的Activity布局上。
还是Window添加的老套路, 故事并没有结束。。。
#####三、将DecorView添加到Window中显示
上面分析完了Dialog中的Window的创建, 布局View的添加, 但是我们的Demo示例代码还有最后一行没有看:dialog.show(),这才是让Dialog真正显示出来的方法, 我们在实际开发中也时常遇到, 就算把Dialog的布局文件都填充好了, 没调用show方法, 一样白忙活, 我们进入这里去看看:
public void show() {
...
mDecor = mWindow.getDecorView();
...
WindowManager.LayoutParams l = mWindow.getAttributes();
...
try {
mWindowManager.addView(mDecor, l);
mShowing = true;
sendShowMessage();
} finally {
}
}
到这里我们发现,和Activity的Window添加过程一样,最终还是通过WindowManager去添加方法到WindowManagerService中, 这样, 我们的Window才真正的展示了出来。
到这里, Dialog的Window创建过程,到最终View的显示 就讲解完了。
#####四、Dialog的关闭
当Dialog被关闭时, 它会通过WindowManager来移除DecorView, 同样最终会通过WindowManagerService来进行移除工作, 毕竟WindowManagerService是所有Window的管理者。这个过程不再赘述。
#####创建Dialog必须使用Activity的Context???
我们在开发中还经常遇到一个问题,如果传递给Dialog的参数不是Activity, 而是用Appliciation的话, 就会报错:
09-21 07:23:36.023: ERROR/AndroidRuntime(1550): FATAL EXCEPTION: main
09-21 07:23:36.023: ERROR/AndroidRuntime(1550): android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
09-21 07:23:36.023: ERROR/AndroidRuntime(1550): at android.view.ViewRoot.setView(ViewRoot.java:509)
从上面的错误信息可以看到,token null is not for an application, 这是因为Application没有应用token, 而应用token一般只有Activity才有。但是, 系统Window比较特殊, 它可以不需要token, 因此,在这个例子中,只需要将对话框指定为系统级Window即可, window的层级上一篇我们已经讲过了, 只需要制定WindowManager.LayoutParams中的type即可, 除此之外, 还需要在MAnifest中声明权限:
dialog.getWindow.setType(LayoutParams.TYPE_SYSTEM_ERROR);
权限声明:
这样, 我们的dialog就可以使用Application了~
喜欢的朋友, 麻烦点个赞吧~~
参考资料: