初识ViewRoot和DecorView
前言
viewRoot对应于ViewRootImpl类,是连接WindowManager和DecorView的枢纽带,View的三个流程均是通过ViewRoot完成的。在ActivityThread中,当Activity对象被创建完毕后,会将DecorView添加到Window中,同时创建ViewRootImpl对象,并将ViewRootImpl和DecorView建立联系,源码如下:
root=new ViewRootImpl(view.getContext,display);
root.setView(view,wparams,panelParentView);
View的绘制由ViewRoot的performTraversals方法开始的。经过measure、layout和draw三个故此才能最终把View绘制出来。
performTraversals
- performMeasure-Measure-Measure
- performLayout-Layout-onLayout
- performDraw-Draw-onDraw
DecorView
DecorView作为顶级的View,一般LinearLayout,包含两个部分,如图:
MeasureSpec:View的尺寸规格
由SpecMode(测量模式)+ SpecSize(规格大小)组成,主要SpecMode有三类
- UNSPECIFIED
父容器对View没有任何的限制,要多大有多大
- EXACTLY
View的精确大小,相当于match_parent
- AT_MOST
View不能大于该值,相当于wrap_content
对于顶级的DecorView,MeasureSpec由自身的LayoutParams和窗口尺寸决定,对于普通的View,MeasureSpec由自身的LayoutParams和父容器的MeasureSpec决定。
View的工作流程
View的工作流程主要由measure、layout、draw三个流程完成,即测量、布局和绘制。
- 对于View:measure:测量,决定View宽/高会调用onMeasure方法:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
我们来看getDefaultSize方法:
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
对于AT_MOST和EXACTLY两种情况,specSize就是View测量后的大小。View的最终大小也是layout阶段决定的。 但是几乎所有情况View的测量大小和最终大小是相等的。
对于UNSPECIFIED这种情况,一般用于系统内部的测量过程,View的大小是getDefaultSize的第一个参数size.即宽和高分别为
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
protected int getSuggestedMinimumHeight() {
return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
}
如果没有给View设置背景,那么就返回View本身的最小宽度mMinWidth
如果给View设置了背景,那么就取View本身最小宽度mMinWidth和背景的最小宽度的最大值
再说下从getDefaultSize方法实现得知:View的大小由specSize决定,我们可以得到以下定论:如果不重写View的onMeasure方法并且设置wrap_content,那么相当于使用match_parent.d对于wrap_content时一般对onMeasure方法作过特殊处理,比如查看TextView的onMeasure方法得知,有
if (widthMode == MeasureSpec.AT_MOST) {
width = Math.min(widthSize, width);
}
if (heightMode == MeasureSpec.AT_MOST) {
height = Math.min(desired, heightSize);
}
- ViewGroup的measure的过程
- ViewGroup,对于自己measure的方法外,还会遍历子元素的measure方法,各个子元素再递归地执行这个过程。和View不同的是,ViewGroup是抽象类,本身并没有去重写View的onMeasure方法,但是提供了一个measureChildren方法,如下:
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
final int size = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < size; ++i) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}
由上面代码,可以看出,ViewGroup在measure方法去多每个子元素measure,measureChildren方法也很简单,如下:
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
final LayoutParams lp = child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
显然可以得知,measureChildren的思想是取出每个子元素的LayParams,然后通过getChildMeasureSpec方法创建子元素的MeasureSpec,直接传给View的measure方法。
我们知道,ViewGroup没有定义测量的过程,并且因为不同的子类有不同的测量过程,故此没有像View对onMeasure方法作统一的实现.比如,LinearLayout的onMeasure方法实现如下:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mOrientation == VERTICAL) {
measureVertical(widthMeasureSpec, heightMeasureSpec);
} else {
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
}
}
今晚先到这里,明天晚上再补上。