美文网首页
Android FrameLayout、RelativeLayo

Android FrameLayout、RelativeLayo

作者: 寺雨九歌 | 来源:发表于2020-10-27 09:50 被阅读0次

    FrameLayout#onMeasure

     @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int count = getChildCount();//获取子view数量
    
            final boolean measureMatchParentChildren =
                    MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
                    MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
            mMatchParentChildren.clear();
    
            int maxHeight = 0;
            int maxWidth = 0;
            int childState = 0;
    
            //对于每一个子View进行遍历,获取最大Height和最大Width
            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);
                            //mMatchParentChildren类型是ArrayList<View>
                        }
                    }
                }
            }
    
            // 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++) {
                    //遍历所有match_parent的子View
                    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);
                }
            }
        }
    

    ① View#resolveSizeAndState

    public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
            final int specMode = MeasureSpec.getMode(measureSpec);
            final int specSize = MeasureSpec.getSize(measureSpec);
            final int result;
            switch (specMode) {
                case MeasureSpec.AT_MOST:
                    if (specSize < size) {
                        result = specSize | MEASURED_STATE_TOO_SMALL;
                    } else {
                        result = size;
                    }
                    break;
                case MeasureSpec.EXACTLY:
                    result = specSize;
                    break;
                case MeasureSpec.UNSPECIFIED:
                default:
                    result = size;
            }
            return result | (childMeasuredState & MEASURED_STATE_MASK);
        }
    

    RelativeLayout#onMeasure

     @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            if (mDirtyHierarchy) {
                mDirtyHierarchy = false;
                sortChildren();
            }
    
            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".
            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) {
                    LayoutParams params = (LayoutParams) child.getLayoutParams();
                    int[] rules = params.getRules(layoutDirection);
    
                    applyHorizontalSizeRules(params, myWidth, rules);
                    measureChildHorizontal(child, params, myWidth, myHeight);
    
                    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);
        }
    

    最终决定RelativeLayout尺寸的是 setMeasuredDimension(width, height),是width和height两个参数。
    myWidth在AT_MOST和EXACTLY模式下取widthSize,UNSPECIFIED为-1,在①处设为DEFAULT_WIDTH;
    myHeight在AT_MOST和EXACTLY模式下取heightSize,UNSPECIFIED为-1;
    width在AT_MOST模式下取myWidth,否则为0;
    height在AT_MOST模式下取myHeight,否则为0;

    mSortedHorizontalChildren
    mSortedVerticalChildren

    RelativeLayout#measureChildHorizontal

    private void measureChildHorizontal(
                View child, LayoutParams params, int myWidth, int myHeight) {
            final int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, params.mRight,
                    params.width, params.leftMargin, params.rightMargin, mPaddingLeft, mPaddingRight,
                    myWidth);//根据给出的值测量出子View的规格
    
            final int childHeightMeasureSpec;
            if (myHeight < 0 && !mAllowBrokenMeasureSpecs) {
                if (params.height >= 0) {
                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                            params.height, MeasureSpec.EXACTLY);
                } else {
                    // Negative values in a mySize/myWidth/myWidth value in
                    // RelativeLayout measurement is code for, "we got an
                    // unspecified mode in the RelativeLayout's measure spec."
                    // Carry it forward.
                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
                }
            } else {
                final int maxHeight;
                if (mMeasureVerticalWithPaddingMargin) {
                    maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom
                            - params.topMargin - params.bottomMargin);
                } else {
                    maxHeight = Math.max(0, myHeight);
                }
    
                final int heightMode;
                if (params.height == LayoutParams.MATCH_PARENT) {
                    heightMode = MeasureSpec.EXACTLY;
                } else {
                    heightMode = MeasureSpec.AT_MOST;
                }
                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, heightMode);
            }
    
            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        }
    

    高度模式为UNSPECIFIED时,设其高度为params.height或0,一般可以不用管;
    高度模式指定时,根据模式和myHeight合成高度规格;
    调用子View的measure方法

    相关文章

      网友评论

          本文标题:Android FrameLayout、RelativeLayo

          本文链接:https://www.haomeiwen.com/subject/gpovjctx.html