推荐思考
- 设置布局时调用的setContentView(@LayoutRes int layoutResID)方法,最终是谁在调用?
- View的事件分发都是按照Activity》ViewGroup》View顺序分发下去,以dispatchTouchEvent为例,讲述Activity到ViewGroup这个过程是如何完成的?
- 一般开发中会在布局再设置一层背景,所以在UI优化时,可以将最顶层的背景去掉,代码为
getWindow().setBackgroundDrawable(null),这里去除的是哪里的背景颜色?
以上三个问题,通过查看源码即可得到答案,感兴趣的朋友可以去探个究竟。
正文
我们先来看一张描述三者之间关系的图
从这张图上看,Window,PhoneWindow,DecorView三者是包含的关系,我们再来看官方文档的介绍:
Abstract base class for a top-level window look and behavior policy. An instance of this class should be used as the top-level view added to the window manager. It provides standard UI policies such as a background, title area, default key processing, etc.
The only existing implementation of this abstract class is android.view.PhoneWindow, which you should instantiate when needing a Window.
提取文档最重要的一点:window是一个抽象类,它唯一的一个实现类是PhoneWindow。
DecorView是PhoneView的一个内部类,继承自FramLayout,所以也是ViewGroup,它是顶层View(用HierarchyViewer工具查看布局的层级时,就能看到DecorView)。
现在总结一下三者的关系:
- 每一个Activity都有一个Window
- Window的唯一实现类是PhoneWindow
- 而PhoneWindow是一个Window,不具备多少View的相关能力,但是PhoneWindow持有DecorView实例,Activity中多数对于View的相关操作都是通过DecorView完成的。 很多源码中可以体现这一点,在关于View的操作上,Activity以及相关联的Window,PhoneView,接受到成员变量,一般都是外抛,交给DecorView去处理。
它们的关系并不复杂,在很多源码中都有体现出来,比如开篇的3个问题,如果去寻找答案,就能在源码里体会到Window,PhoneWindow,DecorView之间的关系。
源码里看三者的关系
我们一起通过开篇的三个问题所涉及的源码,从中去意会三者之间的关系。因源码很多,只贴出了关键的部分。可以自行去看完整源码,看完后一定会更加理解Window,PhoneWindow,DecorView三者之间的关系。
- 设置布局时调用的setContentView(@LayoutRes int layoutResID)方法,最终是由DecorView调用
//Activity的setContentView方法,调用的是getWindow里的setContentView方法
public void setContentView(@LayoutRes int layoutResID) {(
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
//getWindow()方法返回的是mWindow
private Window mWindow;
public Window getWindow() {
return mWindow;
}
//mWindow其实就是PhoneWindow
final void attach(一堆参数) {
...
mWindow = new PhoneWindow(this, window, activityConfigCallback);
...
}
layoutResource是垂直的线性布局,被加载到了DecorView里面,layoutResource有一个FramLyout布局,id为content,Activity的布局就是被设置在了id为Content布局里面,也就是DecorView里面。(这部分的代码很绕,建议自己去查看,这里只贴出关键的代码。)
// PhoneWindow.class
protected ViewGroup generateLayout(DecorView decor) {
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);// R.layout.screen_simple为layoutResource
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
...
return contentParent;
}
- 以dispatchTouchEvent为例, Activity到ViewGroup这个过程的传递,主要源码如下
首先调用getWindow()的superDispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//手机屏保的方法,空实现
onUserInteraction();
}
//事件开始传递
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
然后看getWindow()里的superDispatchTouchEvent方法,调用的是mDecor的superDispatchTouchEvent方法,mDecor就是DecorView。
@Override public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
mDecor的superDispatchTouchEvent方法,是调用父类的DispatchTouchEvent方法。DecorView继承FramLayout,说明它也是ViewGroup,这也就能说明事件是从Activity传递到了ViewGroup。
DecorView的superDispatchTouchEvent方法
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);//调用的是父类的dispatchTouchEvent
}
- 一般开发中会在布局再设置一层背景,所以在UI优化时,可以将最顶层的背景去掉,代码为
getWindow().setBackgroundDrawable(null),这里去除是DecorView的背景颜色。
Window,PhoneWindow,DecorView,或许在实际的开发运用中比较少用到,但是搞懂三者的关系,对于我们学习其它知识点,查看源码,有很大的帮助。