RelativeLayout 测量过程(代码流水线)

来源:互联网 发布:淘宝店铺手机号码修改 编辑:程序博客网 时间:2024/05/18 02:49
  @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        if (mDirtyHierarchy) {            mDirtyHierarchy = false;            sortChildren();        }        // 记录我们已知的尺寸;        /**            View的measure方法里对绘制过程做了一个优化,如果我们或者我们的子View没有要求强制刷新,            而父View给子View的传入值也没有变化(也就是说子View的位置没变化),就不会做无谓的measure。            RelativeLayout要做两次measure,而在做横向的测量时,纵向的测量结果尚未完成,只好暂时使用myHeight传入子View系统            假如子View的Height不等于(设置了margin)myHeight的高度,            那么measure中上面代码所做得优化将不起作用,这一过程将进一步影响RelativeLayout的绘制性能。            优化             如果可以,尽量使用padding代替margin。        **/        int myWidth = -1;        int myHeight = -1;        int width = 0;        int height = 0;        //得到模式        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);        //得到宽高        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);        // Record our dimensions if they are known;        // 记录我们已知的尺寸;        if (widthMode != MeasureSpec.UNSPECIFIED) {            myWidth = widthSize;        }        if (heightMode != MeasureSpec.UNSPECIFIED) {            myHeight = heightSize;        }        //模式        if (widthMode == MeasureSpec.EXACTLY) {            width = myWidth;        }        if (heightMode == MeasureSpec.EXACTLY) {            height = myHeight;        }        View ignore = null;        int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;        final boolean horizontalGravity = gravity != Gravity.START && gravity != 0;        gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;        final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0;        int left = Integer.MAX_VALUE;        int top = Integer.MAX_VALUE;        int right = Integer.MIN_VALUE;        int bottom = Integer.MIN_VALUE;        boolean offsetHorizontalAxis = false;        boolean offsetVerticalAxis = false;        if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {            ignore = findViewById(mIgnoreGravity);        }        final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;        final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;        // We need to know our size for doing the correct computation of children positioning in RTL        // mode but there is no practical way to get it instead of running the code below.        // So, instead of running the code twice, we just set the width to a "default display width"        // before the computation and then, as a last pass, we will update their real position with        // an offset equals to "DEFAULT_WIDTH - width".        /***             我们需要知道我们的尺寸,以便正确计算儿童的位置,在 RTL(从右到左)的布局镜面反射模式             但是没有实际的方法来获得它,而不是运行下面的代码。             因此,我们不是将代码运行两次,而是将宽度设置为“默认显示宽度”。在计算之前,然后作为最后一个通过,我们会更新他们的真实位置             一个偏移量等于  "DEFAULT_WIDTH - width".        **/        final int layoutDirection = getLayoutDirection();        if (isLayoutRtl() && myWidth == -1) {            myWidth = DEFAULT_WIDTH;        }        //水平方向的测量 孩子        View[] views = mSortedHorizontalChildren;        int count = views.length;        for (int i = 0; i < count; i++) {            View child = views[i];            if (child.getVisibility() != GONE) {//只测量非gone的                //孩子的参数                LayoutParams params = (LayoutParams) child.getLayoutParams();                //孩子参数规则                int[] rules = params.getRules(layoutDirection);                /**                尽可能的根据水平方向上的rules给child的childParams的mLeft和mRight设置值                根据理由:                    (1)view隔壁是否有其他view                    (2)view是否对齐 alignWithParent                 根据孩子自己的布局参数,得到rules,给child的childParams的mLeft和mRight设置值                如果,值为VALUE_NOT_SET,则表示这个方向上一个rule都没有,并且值需要根据其他约束推断。                (在positionChildVertical()方法中被推断)                计算得到的数值 后续的步骤中还有可能被调整!                注意的是child的属性优先级的问题。在applyHorizontalSizeRules()                (注意!这里限定在applyHorizontalSizeRules()方法中!)如果同一方向上有多个rules,                那么在applyHorizontalSizeRules()方法中最后判断的那个rule生效。                **/                applyHorizontalSizeRules(params, myWidth, rules);                //测量宽高                measureChildHorizontal(child, params, myWidth, myHeight);                //推断并设置mTop,mBottom中被设置为VALUE_NOT_SET的变量。                if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {                    offsetHorizontalAxis = true;                }            }        }        views = mSortedVerticalChildren;        count = views.length;        final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;        //竖直方向 测量        for (int i = 0; i < count; i++) {            final View child = views[i];            if (child.getVisibility() != GONE) {                final LayoutParams params = (LayoutParams) child.getLayoutParams();                applyVerticalSizeRules(params, myHeight, child.getBaseline());                measureChild(child, params, myWidth, myHeight);                if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {                    offsetVerticalAxis = true;                }                if (isWrapContentWidth) {                    if (isLayoutRtl()) {                        if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {                            width = Math.max(width, myWidth - params.mLeft);                        } else {                            width = Math.max(width, myWidth - params.mLeft - params.leftMargin);                        }                    } else {                        if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {                            width = Math.max(width, params.mRight);                        } else {                            width = Math.max(width, params.mRight + params.rightMargin);                        }                    }                }                if (isWrapContentHeight) {                    if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {                        height = Math.max(height, params.mBottom);                    } else {                        height = Math.max(height, params.mBottom + params.bottomMargin);                    }                }                if (child != ignore || verticalGravity) {                    left = Math.min(left, params.mLeft - params.leftMargin);                    top = Math.min(top, params.mTop - params.topMargin);                }                if (child != ignore || horizontalGravity) {                    right = Math.max(right, params.mRight + params.rightMargin);                    bottom = Math.max(bottom, params.mBottom + params.bottomMargin);                }            }        }        // Use the top-start-most laid out view as the baseline. RTL offsets are        // applied later, so we can use the left-most edge as the starting edge.        View baselineView = null;        LayoutParams baselineParams = null;        for (int i = 0; i < count; i++) {            final View child = views[i];            if (child.getVisibility() != GONE) {                final LayoutParams childParams = (LayoutParams) child.getLayoutParams();                if (baselineView == null || baselineParams == null                        || compareLayoutPosition(childParams, baselineParams) < 0) {                    baselineView = child;                    baselineParams = childParams;                }            }        }        mBaselineView = baselineView;        if (isWrapContentWidth) {            // Width already has left padding in it since it was calculated by looking at            // the right of each child view            width += mPaddingRight;            if (mLayoutParams != null && mLayoutParams.width >= 0) {                width = Math.max(width, mLayoutParams.width);            }            width = Math.max(width, getSuggestedMinimumWidth());            width = resolveSize(width, widthMeasureSpec);            if (offsetHorizontalAxis) {                for (int i = 0; i < count; i++) {                    final View child = views[i];                    if (child.getVisibility() != GONE) {                        final LayoutParams params = (LayoutParams) child.getLayoutParams();                        final int[] rules = params.getRules(layoutDirection);                        if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {                            centerHorizontal(child, params, width);                        } else if (rules[ALIGN_PARENT_RIGHT] != 0) {                            final int childWidth = child.getMeasuredWidth();                            params.mLeft = width - mPaddingRight - childWidth;                            params.mRight = params.mLeft + childWidth;                        }                    }                }            }        }        if (isWrapContentHeight) {            // Height already has top padding in it since it was calculated by looking at            // the bottom of each child view            height += mPaddingBottom;            if (mLayoutParams != null && mLayoutParams.height >= 0) {                height = Math.max(height, mLayoutParams.height);            }            height = Math.max(height, getSuggestedMinimumHeight());            height = resolveSize(height, heightMeasureSpec);            if (offsetVerticalAxis) {                for (int i = 0; i < count; i++) {                    final View child = views[i];                    if (child.getVisibility() != GONE) {                        final LayoutParams params = (LayoutParams) child.getLayoutParams();                        final int[] rules = params.getRules(layoutDirection);                        if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {                            centerVertical(child, params, height);                        } else if (rules[ALIGN_PARENT_BOTTOM] != 0) {                            final int childHeight = child.getMeasuredHeight();                            params.mTop = height - mPaddingBottom - childHeight;                            params.mBottom = params.mTop + childHeight;                        }                    }                }            }        }        if (horizontalGravity || verticalGravity) {            final Rect selfBounds = mSelfBounds;            selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,                    height - mPaddingBottom);            final Rect contentBounds = mContentBounds;            Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds,                    layoutDirection);            final int horizontalOffset = contentBounds.left - left;            final int verticalOffset = contentBounds.top - top;            if (horizontalOffset != 0 || verticalOffset != 0) {                for (int i = 0; i < count; i++) {                    final View child = views[i];                    if (child.getVisibility() != GONE && child != ignore) {                        final LayoutParams params = (LayoutParams) child.getLayoutParams();                        if (horizontalGravity) {                            params.mLeft += horizontalOffset;                            params.mRight += horizontalOffset;                        }                        if (verticalGravity) {                            params.mTop += verticalOffset;                            params.mBottom += verticalOffset;                        }                    }                }            }        }        if (isLayoutRtl()) {            final int offsetWidth = myWidth - width;            for (int i = 0; i < count; i++) {                final View child = views[i];                if (child.getVisibility() != GONE) {                    final LayoutParams params = (LayoutParams) child.getLayoutParams();                    params.mLeft -= offsetWidth;                    params.mRight -= offsetWidth;                }            }        }        setMeasuredDimension(width, height);    }
/***        尽可能的根据水平方向上的rules给child的childParams的mLeft和mRight设置值。    **/    private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) {        RelativeLayout.LayoutParams anchorParams;        // VALUE_NOT_SET indicates a "soft requirement" in that direction. For example:        // left=10, right=VALUE_NOT_SET means the view must start at 10, but can go as far as it        // wants to the right        // left=VALUE_NOT_SET, right=10 means the view must end at 10, but can go as far as it        // wants to the left        // left=10, right=20 means the left and right ends are both fixed        /**            value_not_set表示“软要求”在那个方向            left=10, right=VALUE_NOT_SET 的意思,控件开始于10,可以往右边拓展延伸        **/        childParams.mLeft = VALUE_NOT_SET;        childParams.mRight = VALUE_NOT_SET;        //右边是否存在view        anchorParams = getRelatedViewParams(rules, LEFT_OF);        if (anchorParams != null) {//当前view 有related view;anchorParams 是相关view的参数            //计算得到本view的 mRight            childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +                    childParams.rightMargin);        } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {//右边不存在view            //根据父亲相关参数,得到 mRight            if (myWidth >= 0) {                childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;            }        }        //同理 左边是否存在控件        anchorParams = getRelatedViewParams(rules, RIGHT_OF);        if (anchorParams != null) {            childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin +                    childParams.leftMargin);        } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {            childParams.mLeft = mPaddingLeft + childParams.leftMargin;        }        anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);        if (anchorParams != null) {            childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;        } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {            childParams.mLeft = mPaddingLeft + childParams.leftMargin;        }        anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);        if (anchorParams != null) {            childParams.mRight = anchorParams.mRight - childParams.rightMargin;        } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {            if (myWidth >= 0) {                childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;            }        }        if (0 != rules[ALIGN_PARENT_LEFT]) {            childParams.mLeft = mPaddingLeft + childParams.leftMargin;        }        if (0 != rules[ALIGN_PARENT_RIGHT]) {            if (myWidth >= 0) {                childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;            }        }    }    private void applyVerticalSizeRules(LayoutParams childParams, int myHeight, int myBaseline) {        final int[] rules = childParams.getRules();        // Baseline alignment overrides any explicitly specified top or bottom.        int baselineOffset = getRelatedViewBaselineOffset(rules);        if (baselineOffset != -1) {            if (myBaseline != -1) {                baselineOffset -= myBaseline;            }            childParams.mTop = baselineOffset;            childParams.mBottom = VALUE_NOT_SET;            return;        }        RelativeLayout.LayoutParams anchorParams;        childParams.mTop = VALUE_NOT_SET;        childParams.mBottom = VALUE_NOT_SET;        anchorParams = getRelatedViewParams(rules, ABOVE);        if (anchorParams != null) {            childParams.mBottom = anchorParams.mTop - (anchorParams.topMargin +                    childParams.bottomMargin);        } else if (childParams.alignWithParent && rules[ABOVE] != 0) {            if (myHeight >= 0) {                childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;            }        }        anchorParams = getRelatedViewParams(rules, BELOW);        if (anchorParams != null) {            childParams.mTop = anchorParams.mBottom + (anchorParams.bottomMargin +                    childParams.topMargin);        } else if (childParams.alignWithParent && rules[BELOW] != 0) {            childParams.mTop = mPaddingTop + childParams.topMargin;        }        anchorParams = getRelatedViewParams(rules, ALIGN_TOP);        if (anchorParams != null) {            childParams.mTop = anchorParams.mTop + childParams.topMargin;        } else if (childParams.alignWithParent && rules[ALIGN_TOP] != 0) {            childParams.mTop = mPaddingTop + childParams.topMargin;        }        anchorParams = getRelatedViewParams(rules, ALIGN_BOTTOM);        if (anchorParams != null) {            childParams.mBottom = anchorParams.mBottom - childParams.bottomMargin;        } else if (childParams.alignWithParent && rules[ALIGN_BOTTOM] != 0) {            if (myHeight >= 0) {                childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;            }        }        if (0 != rules[ALIGN_PARENT_TOP]) {            childParams.mTop = mPaddingTop + childParams.topMargin;        }        if (0 != rules[ALIGN_PARENT_BOTTOM]) {            if (myHeight >= 0) {                childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;            }        }    }

【view的测量优化依据】
这里写图片描述

原创粉丝点击