- 初始化app后DecorView调用performTraversals()
- performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);其中childWidthMeasureSpec和childHeightMeasureSpec由 getRootMeasureSpec()方法计算,其中windowSize为屏幕的尺寸,rootDimension默认为-1即MATCH_PARENT
private static int getRootMeasureSpec(int windowSize, int rootDimension) { int measureSpec; switch (rootDimension) { case ViewGroup.LayoutParams.MATCH_PARENT: // Window can't resize. Force root view to be windowSize. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; case ViewGroup.LayoutParams.WRAP_CONTENT: // Window can resize. Set max size for root view. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); break; default: // Window wants to be an exact size. Force root view to be that size. measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); break; } return measureSpec; }
根据上面函数可知,getRootMeasureSpec方法根据DecorView也就是rootview构造子view的measureSpec对象,size为windowSize,即activity里面的layout默认充满整个屏幕,这个没有问题,mode则根据rootDimension进行判断
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); try { mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }
调用view的measure方法- 接下来看view的measure方法里面进行了一系列的判断和缓存工作
if (cacheIndex < 0 || sIgnoreMeasureCache) { // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } else { long value = mMeasureCache.valueAt(cacheIndex); // Casting a long to int drops the high 32 bits, no mask needed setMeasuredDimensionRaw((int) (value >> 32), (int) value); mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; }
经过层层判断,最后调用onMesaure和setMeasuredDimensionRaw,其中setMeasureDimensionRaw如下
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) { mMeasuredWidth = measuredWidth; mMeasuredHeight = measuredHeight; mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET; }
直接给mMeasuredWidth和mMeasuredHeight赋值,代表着该view的measure结束 - onMeasure方法的两个参数都是父view传递进来的,即父view的measureSpec信息
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }
- 获取view的minWidth或者view的BackGround的minWidth,如有有的话。并取其中较大的一个
protected int getSuggestedMinimumWidth() { return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth()); }
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; }
- 这里的size是view自己的,也就是上面getSuggestedMininumWidth的值,而measureSpec则是父view的。
- sepcMode为UNSPRCIFIED,size使用子view的,其他两种使用父view的
- 问题:
- 子view的size只受父view影响?子view的mode不影响自己吗?
- viewgroup的三个measure方法,循环遍历子view调用child的measure方法
- measureChildren遍历子view的measureChild
- measureChild计算padding
- measureChildWithMargins计算padding和margin
- 以measureChildWithMargins为例进行说明:
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 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); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
- 以上方法都调用了getChildMeasureSpec 来计算measureSpec,并最终调用
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
方法进行最终child的measure计算 -
public static int getChildMeasureSpec(int spec, int padding, int childDimension) { int specMode = MeasureSpec.getMode(spec); int specSize = MeasureSpec.getSize(spec); int size = Math.max(0, specSize - padding); int resultSize = 0; int resultMode = 0; switch (specMode) { // Parent has imposed an exact size on us case MeasureSpec.EXACTLY: if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. resultSize = size; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent has imposed a maximum size on us case MeasureSpec.AT_MOST: if (childDimension >= 0) { // Child wants a specific size... so be it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent asked to see how big we want to be case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { // Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } break; } //noinspection ResourceType return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
- 三个参数,spec是父view的measureSpec,padding为子view的margin和padding,childDimension为子view的width和height
- 父view为 EXACTLY
- 子view指定了尺寸,使用子view的,子view未指定尺寸使用父view的,并且子view的mode由自己决定
- 父view为 AT_MOST
- 与上面基本相同,唯一区别是子view为MATCH_PARENT时,子view的mode为AT_MOST,这个很容易理解,此时父view尺寸并不确定,只能得出子view不能大于父view的结论
- UNSPECIFIED
- 子view指定了尺寸,使用子view的,子view未指定尺寸使用父view的,并且子view的mode为UNSPECIFIED
- 综上:viewgroup在计算子view的measureSpec时综合考虑了子view和父viewgroup的measureSpec
- 综上整个view计算流程技术,总结一下
- 初始化时进入到rootview也就是activity的DecorView的 ViewRootImpl类里面的 performTraversals()方法,里面调用了performMeasure 并最终调用 mView.measure方法,其中width和height均为屏幕尺寸,mode默认为 MATCH_PARENT
- 如果view是viewgroup则需要冲写viewGroup的onMesure方法,并调用viewGroup的三个measureChild***方法循环递归遍历子view的measure,其中viewGroup会根据自身和子view的measureSpec共同进行判断计算子view的实际measureSpec并调用view的measure方法:具体计算逻辑如下:
- 父view为 EXACTLY
子view指定了尺寸,使用子view的,子view未指定尺寸使用父view的,并且子view的mode由自己决定 - 父view为 AT_MOST
与上面基本相同,唯一区别是子view为MATCH_PARENT时,子view的mode为AT_MOST,这个很容易理解,此时父view尺寸并不确定,只能得出子view不能大于父view的结论 - 父view为 UNSPECIFIED
子view指定了尺寸,使用子view的,子view未指定尺寸使用父view的,并且子view的mode为UNSPECIFIED
- 父view为 EXACTLY
- view的measure方法调用onMeasure,获取viewGroup传递进来的measureSpec,若mode为UNSPECIFIED 的使用子view的size,该size为mMinWidth和 mBackground.getMinimumWidth()中取较大值
- setMeasuredDimensionRaw 给mMeasuredWidth和 mMeasuredHeight 赋值,计算结束
- measure执行后的结果就是每个view都有了mMeasuredWidth和 mMeasuredHeight,即view知道了自己的大小(当然这个大小不一定是最终的实际大小),有了尺寸下一步就是调用layout来获取view的位置了
- 问题:
- 计算measure尺寸时只考虑了父view和子view的关系,没有考虑父view的多个子view之间的互相影响,这个是不是在viewGroup中特殊处理?
1. performMeasure
最新推荐文章于 2022-04-15 17:26:19 发布