一个应用界面是一个Activity,通过一个实现了Window抽象类的PhoneWindow和DecorView相互关联,DecorView本质上是一个FrameLayout,他有一个唯一的子View,是LinearLayout,包含两个子View,TitleView和ContentView。我们在onCreate(Bundle bundle)方法中调用的setContentView就是填充的这个布局。Window是一个窗口的特别是独立绘制不影响其他界面,不会触发其他界面的输入事件。一个窗口独占一个Surface由WindowManagerService分配,可以把Surface看做一块画布,应用通过Canvas或OpenGL在其上面作画。窗口类包含三个核心组件:
1.WindowManager.LayoutParams:窗口的布局参数
2.Callback:窗口的回调接口,通常由Activity实现
3.ViewTree:窗口所承载的控件树
PhoneWindow:
我们调用setContentView()方法时,实际上就是完成了对所关联的PhoneWindow的ViewTree的设置。
seContentView()解析:
在Activity中调用此方法,实际上是调用了getWindow().setContentView()。getWindow()会返回Activity所关联的PhoneWindow。PhoneWindow的setContentView的源码:
1.会判断ContentView的父容器是否为null,为空的话调用installDecor()生成。
else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
// 具有FEATURE_CONTENT_TRANSITIONS特性表示开启了Transition
// mContentParent不为null,则移除decorView的所有子View
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
// 开启了Transition,做相应的处理,我们不讨论这种情况
// 感兴趣的同学可以参考源码
. . .
} else {
// 一般情况会来到这里,调用mLayoutInflater.inflate()方法来填充布局
// 填充布局也就是把我们设置的ContentView加入到mContentParent中
mLayoutInflater.inflate(layoutResID, mContentParent);
}
在setContentView中,一般会调用LayoutInflater.inflate(...,..)来填充布局。
源码如下:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
. . .
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}//这里的root指的是DectroView
最终调用inflate(XmlPullParser,ViewGroup,boolean)方法来填充布局。在此方法中会进行一些判断:比如读取开始标记,判断父容器是否为null,然后递归调用rInflate(parser,root,inflaterCoontext,attrs,false)填充布局。
ViewRoot
ViewRoot负责View的绘制,每个窗口的dectorView都有一个与之关联的ViewRoot对象,由WindowManager来维护。
在Activity启动时建立dectorView和ViewRoot的关联。
View绘制的起点是联系建立后调用requestLayout()方法,完成界面的初次布局,实际调用的是ViewRootImpl类的requestLayout()方法。在源码中首先检查是否为主线程,然后调用用scheduleTraversals()方法完成一次绘制流程。该方法会向主线程发送一个遍历的消息,最后导致ViewRootImll的performTraversals()方法被调用。