Android中View绘制过程(四) performMeasure

来源:互联网 发布:怎么查看网络是否稳定 编辑:程序博客网 时间:2024/06/05 17:12

performTraversals()功能和名字一致,开始遍历。

方法中首先开始测量,

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);        }    }

调用DecorView的measure方法,之后Decor会遍历调用自己的ChildView的measure,ClidView如果是ViewGroup将遍历自己的ChidView,以此遍历整个ViewTree。

DecorView是一个FramLayout,也就是个ViewGroup,分析DercorView就相当于分析了其他的ViewGroup了,这里的参数childWidthMeasureSpec即屏幕的宽,因为的decorView,这里固定为屏幕的宽高作为参数,如果是其他的ViewGroup则是上一级测量之后,给它的空间了。

ViewGroup中没有measur方法,父类View中有,调用onMeasure,这个ViewGroup中也没有,只能再看当前的FrameLayout了。

@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;        for (int i = 0; i < count; i++) {            final View child = getChildAt(i);            if (mMeasureAllChildren || child.getVisibility() != GONE) {                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);                final LayoutParams lp = (LayoutParams) child.getLayoutParams();                maxWidth = Math.max(maxWidth,                        child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);                maxHeight = Math.max(maxHeight,                        child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);                childState = combineMeasuredStates(childState, child.getMeasuredState());                if (measureMatchParentChildren) {                    if (lp.width == LayoutParams.MATCH_PARENT ||                            lp.height == LayoutParams.MATCH_PARENT) {                        mMatchParentChildren.add(child);                    }                }            }        }        // Account for padding too        maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();        maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();        // Check against our minimum height and width        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());        // Check against our foreground's minimum height and width        final Drawable drawable = getForeground();        if (drawable != null) {            maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());            maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());        }        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),                resolveSizeAndState(maxHeight, heightMeasureSpec,                        childState << MEASURED_HEIGHT_STATE_SHIFT));        count = mMatchParentChildren.size();        if (count > 1) {            for (int i = 0; i < count; i++) {                final View child = mMatchParentChildren.get(i);                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();                final int childWidthMeasureSpec;                if (lp.width == LayoutParams.MATCH_PARENT) {                    final int width = Math.max(0, getMeasuredWidth()                            - getPaddingLeftWithForeground() - getPaddingRightWithForeground()                            - lp.leftMargin - lp.rightMargin);                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(                            width, MeasureSpec.EXACTLY);                } else {                    childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,                            getPaddingLeftWithForeground() + getPaddingRightWithForeground() +                            lp.leftMargin + lp.rightMargin,                            lp.width);                }                final int childHeightMeasureSpec;                if (lp.height == LayoutParams.MATCH_PARENT) {                    final int height = Math.max(0, getMeasuredHeight()                            - getPaddingTopWithForeground() - getPaddingBottomWithForeground()                            - lp.topMargin - lp.bottomMargin);                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(                            height, MeasureSpec.EXACTLY);                } else {                    childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,                            getPaddingTopWithForeground() + getPaddingBottomWithForeground() +                            lp.topMargin + lp.bottomMargin,                            lp.height);                }                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);            }        }    }

在遍历ChildView的时候终于会调用getChildMeasureSpec这个方法,如下

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);    }


在这个方法中分别对,在layout中设置的layout_width,layout_height等作出不同的取值,当MATCH_PARENT=-1,WRAP_CONTENT=-2,和具体数值时的取值。

例如

1)当父ViewGroup节点测量时EXACTLY,如果layout设置的值是>0,则直接使用。Mode也是EXACTLY

2)当父ViewGroup节点测量时EXACTLY,如果layout设置的值MATCH_PARENT,那么测量值就是int size = Math.max(0, specSize - padding);

getChildMeasureSpec()这个方法对所有情况进行了讨论。 这也就是为什么设置MATCH_PARENT,WRAP_CONTENT,系统能知道大小的原因。


因为有很多其他的VIewGroup ,LineraLayout,RelativeLayout,等等,测量的方式也是不一样的。 

这个FramLayout 就遍历了自己的Child,找到最宽的宽度,和最高的高度,作为宽高传到下一级测量。这个符合我们平时使用FrameLayout的现象,如果是WrapContent。DecorVIew传进来值是全屏的,那就是全屏的了。

将自己测量的自己的值遍历传递到下一级,继续测量。

其他的各有自己的测量方法,View不用遍历自己的子View,测量就是自己的大小。例如TextView,ImageView自己的onMeasure会根据情况来setMeasuredDimension()。LinerLayout,RelativeLayout也有自己的遍历计算方式。具体问题要具体分析了。

这样测量下来之后,DecorView根节点下的View全部测量完毕,各个View的变量,mMeasuredWidth,mMeasuredHeight都是已知的了。


下一步就该找找他们的放置的位置了

下一篇performLayout()。