初识ViewRoot和DecorView
ViewRoot:
ViewRoot对应ViewRootImpl类,它是连接windowManager和DecorView的纽带,是个连接器,负责WindowManagerService与DecorView之间的通信,View的三大流程(测量(measure),布局(layout),绘制(draw))均通过ViewRoot来完成。
ViewRoot并不属于View树的一份子。从源码实现上来看,它既非View的子类,也非View的父类,但它实现了ViewParent接口,这让它可以作为View的名义上的父视图。Android的所有触屏事件、按键事件、界面刷新等事件都是通过ViewRoot进行分发的。
下面我们看一下ViewRootImpl的产生过程
源码WindowManagerImpl
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
...
@Override
public void addView(View view, ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
}
源码WindowManagerGlobal#addView
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
......
synchronized (mLock) {
ViewRootImpl root;
//实例化一个ViewRootImpl对象
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
......
try {
//把DecorView和ViewRootImpl 建立起联系
root.setView(view, wparams, panelParentView);
}catch (RuntimeException e) {
}
}
}
我们看到实例化了ViewRootImpl对象,然后调用其setView()方法。其中setView()方法经过一些列折腾,最终调用了performTraversals()方法(performTraversals:执行遍历),然后依照下图流程层层调用,完成绘制,最终界面才显示出来。
其中:measure用来测量view的宽高,Layout用来确定View在父容器中放置的位置,即view4个顶点的坐标和实际View的宽高,draw负责把View绘制在屏幕上,
performTraversals的大致流程是这样的:
performTraversals会依次调用performMeasure,performLayout,performDraw三个方法,这3个方法分别完成顶级ViewGroup的
Measure,Layout,Draw. 这3大流程,其中performMeasure会调用Measure方法来测量自己,因为是ViewGroup,除了测量自己外,还要测量子孩子,所以在Measure方法中会调用onMeasure方法,在onMeasure方法中会对所有的子孩子进行Measure过程,这个时候Measure过程就从父容器传到子元素啦,这样就完成了一次Measure过程,接着子元素就重复父元素的过程,如此反复就完成了整个View树的遍历,同理performLayout和performDraw的传递流程和performMeasure类似,唯一不同的是,performDraw的传递过程是在draw方法中通过dispatchDraw来实现的(而不是onDraw())
DecorView:
DecorView作为顶级View,它是继承于 frameLayout,它内部会包含一个熟直方向的Linarlayout,Linarlayout里有上下两部分(具体情况和android版本和主题有关),上面是标题栏,下面是内容栏…在activity中,我们通过setContentView所设置的布局其实就是被加载在内容栏上,setContentView所对应的View 可以通过这种方式得到findViewByID(android.R.id.content).getChildAt(0)