view -measure过程

1 measure原理

1.ViewRootImpl.performTraversals是系统进行view树遍历的核心函数,该函数内部会依次执行measure.layout.draw过程.


view -measure过程_第1张图片
1.png

2.对于view的绘制,最后都是通过Canvas来实现的.而Canvas的大小是无限而.我们设定view的大小是为了在Canvas绘制时指定一个固定的区域来绘制某个view.

3.layout_width,layout_heigh,设置的值标识父视图给该view分配的窗口大小.而不是view的大小.

4.measure过程是从ViewRootImpl中调用host.measure()开始.然后调用到View.measure()函数View.measure函数被final修饰,无法被子类覆盖.表示系统不希望我们重写修改View.measure()函数View.measure()函数中会调用到onMeasure()函数.而host一般都是ViewGroup.并实现了onMeasure()函数.需要在ViewGroup.onMeasure()函数中完成对子视图的measure过程. 系统为ViewGroup提供了 measureChild,measureChildWithMargins方法来协助完成子视图的measure过程.


view -measure过程_第2张图片
2.png

5.视图的最终测量.是调用 View.setMeasuredDimension(int measuredWidth, int measuredHeight)来完成.int参数由两部分组成. int的前两位表示测量方式(UNSPECIFIED,EXACTLY,AT_MOST) ,int 后30位 表示测量的值. 通过下边的方法拿到测量值和测量的方式.
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
6.对于最根上的host.measure的参数.他的测量值就是窗口本身的大小.他的测量方式是EXACTLY.视图的窗口大小是有由视图和他的父视图共同决定的.既每个view通过他自己的layout_height和他父视图Mode和Size的一起决定该视图的Mode和Size;
6.ViewGroup提供measureChildWithMargins,measureChildren,measureChild三个方法辅助测量子类.这三个方法本质一样.measureChildWithMargins 提供了对margin和padding的处理.接下来看ViewGroup.measureChildWithMargins方法

        protected void measureChildWithMargins(View child,
            int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();//1.得到view的layoutparams

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);//2.得到子视图宽度的最终值(包含mode和size)
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                        + heightUsed, lp.height);//3.得到子视图高度的最终值(包含mode和size)

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);//4.设置子视图的测量大小.
    }
 在第一步中.得到child的layoutParams并强转为MarginLayoutParams,是因为view.getLayoutParams()返回的LayoutParams参数是在view被
viewGroup调用addView()时为view添加的.添加的LayoutParams通常都是继承自MarginLayoutParams.

第四步是所有view的最终设置方法.
第二第三步是相同的.看第二步,主要操作就根据父视图的宽度mode和size及子视图的layout_parems(lp.width)来最终完成子视图的宽高.如下.

 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 = 0;
            resultMode = MeasureSpec.UNSPECIFIED;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size.... find out how
            // big it should be
            resultSize = 0;
            resultMode = MeasureSpec.UNSPECIFIED;
        }
        break;
    }
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

对应关系如下


view -measure过程_第3张图片
3.png

2 LinearLayout的onMeasure原理

1.看measureVertical(int widthMeasureSpec, int heightMeasureSpec) 垂直方向的测量

    void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
    //1.第一部分.计算所有视图的高度.
        mTotalLength = 0;//总高度
        float totalWeight = 0; /总weight
        final int count = getVirtualChildCount();//子视图数量
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec); //父视图的宽的模式
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);

         for (int i = 0; i < count; ++i) {
            final View child = getVirtualChildAt(i);
            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();

            totalWeight += lp.weight;
            //这里一段是对含有layout_weight的view的处理.如果父视图的model是EXACTLY,那么当其他子视图占满父视图高度时,
        wetght>0的子视图可能分配不到空间,对weight大于0的子视图跳过测量
            if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
                final int totalLength = mTotalLength;
                mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
                skippedMeasure = true;
            } else {
                int oldHeight = Integer.MIN_VALUE;
                if (lp.height == 0 && lp.weight > 0) {
                    oldHeight = 0;
                    lp.height = LayoutParams.WRAP_CONTENT;
                }

              //调用measureChildWithMargins 对子视图进行测量.
                measureChildBeforeLayout(
                       child, i, widthMeasureSpec, 0, heightMeasureSpec,
                       totalWeight == 0 ? mTotalLength : 0);


                final int childHeight = child.getMeasuredHeight();
                final int totalLength = mTotalLength;
                mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
                       lp.bottomMargin + getNextLocationOffset(child));
            }
 
 
          //对宽度的处理.
            final int margin = lp.leftMargin + lp.rightMargin;
            final int measuredWidth = child.getMeasuredWidth() + margin;
            maxWidth = Math.max(maxWidth, measuredWidth);
            childState = combineMeasuredStates(childState, child.getMeasuredState());

            allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
            if (lp.weight > 0) {
                weightedMaxWidth = Math.max(weightedMaxWidth,
                        matchWidthLocally ? margin : measuredWidth);
            } else {
                alternativeMaxWidth = Math.max(alternativeMaxWidth,
                        matchWidthLocally ? margin : measuredWidth);
            }
            i += getChildrenSkipCount(child, i);
        }

        if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
            mTotalLength += mDividerHeight;
        }
      //这段忽略.
        if (useLargestChild &&
                (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
            mTotalLength = 0;

            for (int i = 0; i < count; ++i) {
                final View child = getVirtualChildAt(i);

                if (child == null) {
                    mTotalLength += measureNullChild(i);
                    continue;
                }

                if (child.getVisibility() == GONE) {
                    i += getChildrenSkipCount(child, i);
                    continue;
                }

                final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
                        child.getLayoutParams();
                // Account for negative margins
                final int totalLength = mTotalLength;
                mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
                        lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
            }
        }

       //对高度的进一步计算,这里判断父视图还有没有空间,把剩余空间分配给weight>0的子视图,这里会测量weight>0的子视图
        mTotalLength += mPaddingTop + mPaddingBottom;
        int heightSize = mTotalLength;
        heightSize = Math.max(heightSize, getSuggestedMinimumHeight());\
      //这个方法获得子视图最终能占用的布局大小.
        int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
        heightSize = heightSizeAndState & MEASURED_SIZE_MASK;
        //heightSize 表示父视图能提供的并且子视图肯定会占用完的高度,
         //mTotalLength表示所有子视图的高度
        //两者的差 delta如果大于0,表示有剩余空间,会分配给weight>0的子视图,如果小delta<0,标识空间不够,则需要weight>0的子视图腾出空间.
        int delta = heightSize - mTotalLength;
        if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
            float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
            mTotalLength = 0;
 
            for (int i = 0; i < count; ++i) {
                final View child = getVirtualChildAt(i);
                LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
                
                float childExtra = lp.weight;
                if (childExtra > 0) {
                    // share 就是根据weight 额外分配的空间,可能是负值,正值表示增加,负值标识减少.
                    int share = (int) (childExtra * delta / weightSum);
                    weightSum -= childExtra;
                    delta -= share;
                    final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                            mPaddingLeft + mPaddingRight +
                                    lp.leftMargin + lp.rightMargin, lp.width);

                    if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
                        int childHeight = child.getMeasuredHeight() + share;//将额外的空间交给子视图
                        if (childHeight < 0) {
                            childHeight = 0;
                        }
                                                child.measure(childWidthMeasureSpec,
                                MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
                    } else {
                        child.measure(childWidthMeasureSpec,
                                MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,
                                        MeasureSpec.EXACTLY));
                    }
                }
                final int totalLength = mTotalLength;
                mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
                        lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
            }

           
            mTotalLength += mPaddingTop + mPaddingBottom;
 
        } else {
            alternativeMaxWidth = Math.max(alternativeMaxWidth,
                                           weightedMaxWidth);
            / /再一次进行测量.在第一次measure过的就不需要测量.对有所weight>0的再次进行分配.--这段我也没太懂.
            if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
                for (int i = 0; i < count; i++) {
                    final View child = getVirtualChildAt(i);

                    final LinearLayout.LayoutParams lp =
                            (LinearLayout.LayoutParams) child.getLayoutParams();

                    float childExtra = lp.weight;
                    if (childExtra > 0) {
                        child.measure(
                                MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
                                        MeasureSpec.EXACTLY),
                                MeasureSpec.makeMeasureSpec(largestChildHeight,
                                        MeasureSpec.EXACTLY));
                    }
                }
            }
        }

        if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
            maxWidth = alternativeMaxWidth;
        }
        
        maxWidth += mPaddingLeft + mPaddingRight;

        // Check against our minimum width
        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
        
        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                heightSizeAndState);
 
    }
``` measures 结束.


你可能感兴趣的:(view -measure过程)