FrameLayout布局绘制流程解析

最近在深入学习Android视图的绘制流程,从相对简单的FramgLayout开始。
onMeasure函数主要是用来计算子视图以及ViewGroup自身的大小,代码如下:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int count = getChildCount();

    //如果width或者height的mode有一个不为EXACTLY那么使能measureMatchParentChildren
    final boolean measureMatchParentChildren =
            MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
            MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
    //清除mMatchParentChildren缓存,mMatchParentChildren中保存了前一次onMeasure中宽或高被设置为match_parent的view
    mMatchParentChildren.clear();

    int maxHeight = 0;
    int maxWidth = 0;
    int childState = 0;

    //step1.遍历计算所有子View大小
    for (int i = 0; i < count; i++) {
        final View child = getChildAt(i);
        //mMeasureAllChildren如果使能,那么无视可见性,强制measure所有视图,默认false
        if (mMeasureAllChildren || child.getVisibility() != GONE) {
            //measure子View,该函数属于ViewGroup
            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());
            //如果measureMatchParentChildren使能那么
            //向mMatchParentChildren中加入所有高或宽为Match_Parent的view
            if (measureMatchParentChildren) {
                if (lp.width == LayoutParams.MATCH_PARENT ||
                        lp.height == LayoutParams.MATCH_PARENT) {
                    mMatchParentChildren.add(child);
                }
            }
        }
    }

    // Account for padding too
    // 考虑padding
    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());
    }
    //step2.设置FrameLaout自身的尺寸
    setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
            resolveSizeAndState(maxHeight, heightMeasureSpec,
                    childState << MEASURED_HEIGHT_STATE_SHIFT));

    //step3.绘制宽或高为Match_Parent的view
    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();
            int childWidthMeasureSpec;
            int childHeightMeasureSpec;

            if (lp.width == LayoutParams.MATCH_PARENT) {
                childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() -
                        getPaddingLeftWithForeground() - getPaddingRightWithForeground() -
                        lp.leftMargin - lp.rightMargin,
                        MeasureSpec.EXACTLY);
            } else {
                childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                        getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
                        lp.leftMargin + lp.rightMargin,
                        lp.width);
            }

            if (lp.height == LayoutParams.MATCH_PARENT) {
                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() -
                        getPaddingTopWithForeground() - getPaddingBottomWithForeground() -
                        lp.topMargin - lp.bottomMargin,
                        MeasureSpec.EXACTLY);
            } else {
                childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
                        getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
                        lp.topMargin + lp.bottomMargin,
                        lp.height);
            }

            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        }
    }
}

// 考虑子视图外边距计算子视图大小
protected void measureChildWithMargins(View child,
            int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
    final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

    //根据parentWidthMeasureSpec,ViewGroup剩余宽度,child的宽度重新计算child的widthMeasureSpec
    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
            mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                    + widthUsed, lp.width);
    //根据parentHeightMeasureSpec,ViewGroup剩余高度度,child的高度重新计算childHeightMeasureSpec
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
            mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                    + heightUsed, lp.height);
    //调用measure计算大小
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

onLayout计算子视图的位置:

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    // 直接调用了FrameLayout中的layoutChildren方法
    layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}

//该方法中的参数其实都是相对于父视图的
void layoutChildren(int left, int top, int right, int bottom,
                                  boolean forceLeftGravity) {
    final int count = getChildCount();

    // 获取各个方向的padding值
    final int parentLeft = getPaddingLeftWithForeground();
    final int parentRight = right - left - getPaddingRightWithForeground();

    final int parentTop = getPaddingTopWithForeground();
    final int parentBottom = bottom - top - getPaddingBottomWithForeground();

    mForegroundBoundsChanged = true;

    // 循环layout所有子View
    for (int i = 0; i < count; i++) {
        final View child = getChildAt(i);
        // 不绘制可见性为GONE的View
        if (child.getVisibility() != GONE) {
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();

            final int width = child.getMeasuredWidth();
            final int height = child.getMeasuredHeight();

            int childLeft;
            int childTop;

            int gravity = lp.gravity;
            if (gravity == -1) {
                gravity = DEFAULT_CHILD_GRAVITY;
            }
            // 获取布局方向,可能的值为rtl、ltr,分别为从右至左、从左至右布局
            final int layoutDirection = getLayoutDirection();
            // 结合layoutDirection和View的layout_gravity计算出真正的gravity
            // Gravity.getAbsoluteGravity是一个静态方法
            // 源码注释:
            // if horizontal direction is LTR, then START will set LEFT and END will set RIGHT.
            // if horizontal direction is RTL, then START will set RIGHT and END will set LEFT.
            // 水平方向如果为从左到右,那么,Gratity为START会被设置成LEFT,END会被设置成RIGHT
            // 水平方向如果为从右到左,那么,Gratity为START会被设置成RIGHT,END会被设置成LEFT
            final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
            // 获取垂直方向的Gravity
            final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;

            // 根据不同的水平比重计算View的水平坐标
            switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                case Gravity.CENTER_HORIZONTAL:
                    childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
                    lp.leftMargin - lp.rightMargin;
                    break;
                case Gravity.RIGHT:
                    if (!forceLeftGravity) {
                        childLeft = parentRight - width - lp.rightMargin;
                        break;
                    }
                case Gravity.LEFT:
                default:
                    childLeft = parentLeft + lp.leftMargin;
            }

            //根据垂直比重计算View的垂直坐标
            switch (verticalGravity) {
                case Gravity.TOP:
                    childTop = parentTop + lp.topMargin;
                    break;
                case Gravity.CENTER_VERTICAL:
                    childTop = parentTop + (parentBottom - parentTop - height) / 2 +
                    lp.topMargin - lp.bottomMargin;
                    break;
                case Gravity.BOTTOM:
                    childTop = parentBottom - height - lp.bottomMargin;
                    break;
                default:
                    childTop = parentTop + lp.topMargin;
            }

            child.layout(childLeft, childTop, childLeft + width, childTop + height);
        }
    }
}

FrameLayout并没有继承onDraw方法,而是继承了draw:

// 仅仅绘制了前景图片
public void draw(Canvas canvas) {
    super.draw(canvas);

    if (mForeground != null) {
        final Drawable foreground = mForeground;
        // 根据mForegroundBoundsChanged判断是否重新设置界限
        // mForegroundBoundsChanged在onLayout以及onSizeChanged方法中会被使能
        if (mForegroundBoundsChanged) {
            mForegroundBoundsChanged = false;
            final Rect selfBounds = mSelfBounds;
            final Rect overlayBounds = mOverlayBounds;

            final int w = mRight-mLeft;
            final int h = mBottom-mTop;
            // 根据mForegroundInPadding判断前景是否被padding所束缚,重新设置bounds
            if (mForegroundInPadding) {
                selfBounds.set(0, 0, w, h);
            } else {
                selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom);
            }

            final int layoutDirection = getLayoutDirection();
            Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(),
                    foreground.getIntrinsicHeight(), selfBounds, overlayBounds,
                    layoutDirection);
            foreground.setBounds(overlayBounds);
        }

        foreground.draw(canvas);
    }
}

以上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值