参考文章 :
目标
1. measure();
2. layout();
3. draw();
在进行源码分析之前, 先根据setContentView源码分析流程画出一个Activity布局图;

为了展示的方便, 上图在层级之间留了空白;
一、ViewRootImpl绘制流程起始点
1.1 ViewRootImpl.requestLayout
@Override
public void requestLayout() {
// mHandlingLayoutInLayoutRequest为测量控制器, 测量中置为true, 结束或未开始测量置为false;
if (!mHandlingLayoutInLayoutRequest) {
// 1. 为了保证View的测量绘制单线程执行, 在测量开始之前, 需要检测当前线程;模块<1.6>
// 2. 线程安全之后才会进行测量布局绘制等操作;模块<1.7>
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
1.2 ViewRootImpl.checkThread:
void checkThread() {
// 1. 注意这里的检测方式, 并不是使用的"main".Thread.currentThread(), 而是将我们在初始化ViewRootImpl
// 时所在的线程保存下来, 然后在这里与当前线程进行比较, 如果是同一个线程, 则不会抛异常, 反之抛出异常,
// 然后注意异常的提示内容: 当前线程不是创建ViewRootImpl的线程;
// 2. 所以其实通常所说不能在非主线程中做UI操作, 以及不能在非UI线程中操作UI其实不算准确, 但是为何这句话
// 这么流行, 因为onXXX的回调都是在主线程中执行, 在这条线程里执行onResume时会初始化ViewRootImpl,
// 所以这里的ViewRootImpl持有主线程的Thread;
// 3. 1和2说的感觉又像是废话, 归根结底就是一句话, 不要在非ViewRootImpl初始化线程中操作ViewRootImpl
// 下的View;
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
1.3 ViewRootImpl.performTraversals:
private void performTraversals() {
// mView这里指向的是DecorView, 将DecorView赋值给host;
final View host = mView;
WindowManager.LayoutParams lp = mWindowAttributes;
int desiredWindowWidth;
int desiredWindowHeight;
Rect frame = mWinFrame;
if (mWidth != frame.width() || mHeight != frame.height()) {
// frame与SurfaceHolder有关, 为当前View附属的区域的大小, 也就是当前DecorView的大小;
mWidth = frame.width();
mHeight = frame.height();
}
// 1. 通过getRootMeasureSpec算出ChildMeasureSpec, 即DecorView的MeasureSpec;
// 2. getRootMeasureSpec内部分析可知, 此时DecorView的mode只有两种模式EXACTLY/AT_MOST;
// 3. 而结合实际情况, DecorView的specMode为MeasureSpec.EXACTLY;
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
// childWidthMeasureSpec与childHeightMeasureSpec其实均为DecorView的MeasureSpec;
// 通过performMeasure测量计算DecorView及其子类的MeasureSpec;
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
// host指向跟节点, 对应Activity为DecorView;
int width = host.getMeasuredWidth();
int height = host.getMeasuredHeight();
boolean measureAgain = false;
...
layoutRequested = true;
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
if (didLayout) {
// 测量完成之后对View进行布局;
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
}
// 测量完成之后对View进行渲染;
performDraw();
mIsInTraversal = false;
}
1.4 ViewRootImpl.getRootMeasureSpec
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
// 此时rootDimension指的实际为DecorView的尺寸, 而DecorView的尺寸为确定值;
// (1) layout_width指定为MATCH_PARENT, 由于显示区域的尺寸的确定的, 所以此时DecorView的size
// 为确定值, mode为EXACTLY;
// (2) layout_width指定为WRAP_CONTENT, DecorView的尺寸为不确定值, 但是他又有一个极限值, 不能
// 超过父类规定的最大尺寸, 所以模式为AT_MOST;
// (3) layout_width明确指定了值, 则size取xml中设置的值, 模式为EXACTLY;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
}
1.5 ViewRootImpl.performMeasure:
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
// 结合上文可知这里传入的childWidthMeasureSpec/childHeightMeasureSpec只有两种情况,
// 既MeasureSpec.EXACTLY/MeasureSpec.AT_MOST
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
二、测量(Measure) :
2.1 FrameLayout.onMeasure:
DecorView继承自FrameLayout, 而measure为final类型, 只有onMeasure允许被子类重写;
// 1.再次强调测量是从根节点开始,而根节点指的是DecorView,而非Activity.setContentView
// 中传入的布局的根节点;
// 2.widthMeasureSpec与heightMeasureSpec均为当前节点的父节点的参数;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
final boolean measureMatchParentChildren =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
mMatchParentChildren.clear();
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
// 遍历子View, 对子View进行测量;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
// 遍历子View(非GONE状态), INVISIBLE虽然也不可见, 但是有占位的作用, 所以也需要进行测量;
if (mMeasureAllChildren || child.getVisibility() != GONE) {
// 1.这里采用的递归的方式, 递归的出口是直到childView为View类型或者childView的childCount==0;
// 2.否则就是根据parentMeasureSpec以及自身size获取自身的measureSpec;
// 3.然后在根据自己的measureSpec去测量childView的measureSpec;
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
// 取所有ChildView的size的最大值;
maxWidth = Math.max(maxWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
...
}
}
// for循环结束也就是表示所有的View的测量工作都完成了, 接下来开始从最底层依次获取每个View的实际尺寸;
// 计算maxWidth/maxHeight, 为后边ParentView测量自身作准备;
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
...
// 根据childView的最大值设置自身尺寸的最大值;
setMeasuredDimension(
resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT));
count = mMatchParentChildren.size();
...
}
2.2 ViewGropu.measureChildWithMargins:
// parentWidthMeasureSpec: 当前View的父View对该View的约束;
// parentHeightMeasureSpec: 当前View的父View对该View的约束;
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
// 根据parentMeasureSpec算出childMeasureSpec;
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed,
lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed,
lp.height);
// 1.根据下文对getChildMeasureSpec()方法的分析可知, 对child进行测量并不表示获取到了childView的
// 精确大小, 而仅仅是根据parentView的约束拿到了parentView对自己的约束值;
// 2.此时再根据该约束值去测量自己的childView;
// 3.如果child为View类型, 则调用View.onMeasure方法, 如果child为GroupView类型, 则调用
// ViewGroup.onMeasure方法;
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
2.3 ViewGropu.getChildMeasureSpec
// spec: 当前View的父View对该View的约束;
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
// 1. 算出ParentView的mode;
// 2. 算出ParentView的size;
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
// 通过ParentView尺寸 - ParentPadding - ChildMargin算出ChildView的尺寸;
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
// specSize是当前View的parentView的specSize;
switch (specMode) {
case MeasureSpec.EXACTLY://如果parentView尺寸为精确值时;
if (childDimension >= 0) {
// 1.如果childSize此时有尺寸, 则childMode尺寸模式此时也是精确值;
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// 2.如果此时childSize的尺寸与parentSize一致, 那么此时由于parentSize是精确值, 所以此时
// childSize的尺寸也是精确值, childMode自然也是精确模式;
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// 3.如果此时childSize是包裹值, 但是parentSize是精确值, 所以先给childSize设置一个临时的值,
// 但是该临时的值有上限, 所以模式为AT_MOST;
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
case MeasureSpec.AT_MOST:// 如果此时parentMode为AT_MOST模式, 表示此时parentSize其实为不确定值;
if (childDimension >= 0) {
// 1.如果childSize此时有尺寸, 则childMode尺寸模式此时也是精确值;
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// 2.如果此时childSize为MATCH_PARENT, 但是parentSize此时为不确定值, 所以此时childSize也是
// 不确定值, 所以此时childMode为AT_MOST;
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// 3.如果此时childSize是包裹值, parentSize也是包裹值, 所以先给childSize设置一个临时的值,
// 但是该临时的值有上限, 所以模式为AT_MOST;
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
case MeasureSpec.UNSPECIFIED:// 此时parentMode为MeasureSpec.UNSPECIFIED, 表示parentView大小为任意值;
if (childDimension >= 0) {
// 1.如果childSize此时有尺寸, 则childMode尺寸模式此时也是精确值;
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// 由于parentSize为任意值, 而childSize此时又表示随parentSize的尺寸, 所以childSize也是任意值;
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
2.4 View.onMeasure:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/**
* 对当前View的mMeasuredWidth和mMeasuredHeight进行赋值, 结合下面代码可知, mMeasuredWidth
* 和mMeasuredHeight并不一定精确, 在实际运行中可能会动态变化;
*/
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
/**
* measureSpec是根据ParentMode, ParentSize, ChildSize一起算出来的;
*/
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
/**
* 1. 结合前几个模块可知, 其实只有模式为EXACTLY时, ChildView的size才是精确的, 另外两种模式下
* 的result都只能算作是参考值;
*/
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
三、布局(layout) :
3.1 ViewRootImpl.performLayout:
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight) {
mLayoutRequested = false;
mScrollMayChange = true;
mInLayout = true;
final View host = mView;
// host指向DecorView, 切入到FrameLayout;模块<3.2>
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
...
mInLayout = false;
}
3.2 FrameLayout.layout:
View.class:
// ViewGroup.layout直接调用了View.layout;
public void layout(int l, int t, int r, int b) {
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
// 计算子布局是否发生了变化;
boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
/**
* onLayout被调用有两个情况:
* 1. changed = true;
* 2. 显示调用requestLayout()触发mPrivateFlags &= PFLAG_LAYOUT_REQUIRED运算;
*/
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
// 对ChildView进行布局;模块<3.3>
onLayout(changed, l, t, r, b);
}
}
3.3 FrameLayout.onLayout:
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false);
}
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
final int count = getChildCount();
final int parentLeft = getPaddingLeftWithForeground();
final int parentRight = right - left - getPaddingRightWithForeground();
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft;
int childTop;
int gravity = lp.gravity;
if (gravity == -1) {
gravity = DEFAULT_CHILD_GRAVITY;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
if (!forceLeftGravity) {
childLeft = parentRight - width - lp.rightMargin;
break;
}
case Gravity.LEFT:
default:
childLeft = parentLeft + lp.leftMargin;
}
switch (verticalGravity) {
case Gravity.TOP:
childTop = parentTop + lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = parentTop + (parentBottom - parentTop - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentBottom - height - lp.bottomMargin;
break;
default:
childTop = parentTop + lp.topMargin;
}
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
网友评论