1. 程式人生 > >RelativeLayout 測量過程(程式碼流水線)

RelativeLayout 測量過程(程式碼流水線)

  @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的測量優化依據】
這裡寫圖片描述