View的工作流程--layout过程

调用流程

layout流程是从performTraversals()开始的,performTraversals()在调用完测量流程后,又调用了performLayout(),这是layout流程的起点。

ViewRootImpl # performLayout()

    private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
        mLayoutRequested = false;
        mScrollMayChange = true;
        mInLayout = true;
        // host就是DecorView
        final View host = mView;
        if (host == null) {
            return;
        }
        ...
        try {
            // 传入窗口四个顶点的坐标
            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

            ...
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        mInLayout = false;
    }

因为DecorView本身并没有重写layout(),它的父类FrameLayout也没有重写,所以调用的是ViewGroup的layout()

ViewGroup # layout()

    @Override
    public final void layout(int l, int t, int r, int b) {
        if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
            // 处理动画
            if (mTransition != null) {
                mTransition.layoutChange(this);
            }
            // 调用View的layout()
            super.layout(l, t, r, b);
        } else {
            // record the fact that we noop'd it; request layout when transition finishes
            mLayoutCalledWhileSuppressed = true;
        }
    }

View # layout()

    public void layout(int l, int t, int r, int b) {
        if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
            onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        }

        int oldL = mLeft;
        int oldT = mTop;
        int oldB = mBottom;
        int oldR = mRight;
        // setFrame()将传入的窗口坐标设置成全局变量
        boolean changed = isLayoutModeOptical(mParent) ?
                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

        if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
            // 分发layout
            onLayout(changed, l, t, r, b);

            ...
        }
        ...
    }

注意!!!此时调用的是DecorView的onLayout(),它又调用了父类的onLayout()

DecorView # onLayout()

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    // ...
}

FrameLayout # onLayout()

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        // 开始分发处理子View的layout
        layoutChildren(left, top, right, bottom, false /* no force left gravity */);
    }

FrameLayout # layoutChildren()

    void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
        final int count = getChildCount();

        final int parentLeft = getPaddingLeftWithForeground();
        final int parentRight = right - left - getPaddingRightWithForeground();

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

        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            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;
                }

                final int layoutDirection = getLayoutDirection();
                final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
                final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
                // 从水平和竖直方向对变量做处理
                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;
                }

                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;
                }
                // 遍历子View,调用子View的layout()
                child.layout(childLeft, childTop, childLeft + width, childTop + height);
            }
        }
    }

View和ViewGroup的相关方法

View # layout()

    public void layout(int l, int t, int r, int b) {
        if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
            onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        }
        // mXXX是全局变量(此View相对于父View的相对坐标)先保存下来
        int oldL = mLeft;
        int oldT = mTop;
        int oldB = mBottom;
        int oldR = mRight;
        // setFrame()将传入的窗口坐标赋值成全局变量mXXX
        // 四个顶点确定了,View的位置就确定了
        boolean changed = isLayoutModeOptical(mParent) ?
                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

        if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
            
            onLayout(changed, l, t, r, b);

            ...
        }
        ...
    }

View # setFrame()

    protected boolean setFrame(int left, int top, int right, int bottom) {
        boolean changed = false;

        if (DBG) {
            Log.d(VIEW_LOG_TAG, this + " View.setFrame(" + left + "," + top + ","
                    + right + "," + bottom + ")");
        }

        if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
            changed = true;

            // Remember our drawn bit
            int drawn = mPrivateFlags & PFLAG_DRAWN;

            int oldWidth = mRight - mLeft;
            int oldHeight = mBottom - mTop;
            int newWidth = right - left;
            int newHeight = bottom - top;
            boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);

            // Invalidate our old position
            invalidate(sizeChanged);
            // 重新赋值
            mLeft = left;
            mTop = top;
            mRight = right;
            mBottom = bottom;
            mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);

            mPrivateFlags |= PFLAG_HAS_BOUNDS;
            ...
        }
        return changed;
    }

View # onLayout()

没有做统一实现,具体实现和具体布局有关

    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    }

ViewGroup # layout()

    @Override
    public final void layout(int l, int t, int r, int b) {
        if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
            // 处理动画
            if (mTransition != null) {
                mTransition.layoutChange(this);
            }
            // 调用View的layout()
            super.layout(l, t, r, b);
        } else {
            // record the fact that we noop'd it; request layout when transition finishes
            mLayoutCalledWhileSuppressed = true;
        }
    }

ViewGroup # onLayout()

ViewGroup是一个抽象类,继承它的类都要实现onLayout()

    @Override
    protected abstract void onLayout(boolean changed,
            int l, int t, int r, int b);

测量宽高和实际宽高

获取测量宽高

width = view.getMeasuredWidth();
height = view.getMeasuredHeight();

public final int getMeasuredWidth() {
    return mMeasuredWidth & MEASURED_SIZE_MASK;
}

获取实际宽高

width = view.getWidth();
height = view.getHeight();

public final int getWidth() {
    return mRight - mLeft;
}

区别

mLeft、mRight、mTop、mBottom这四个值是从layout()的参数而来的,而大部分调用layout时,传的参数都是根据getMeasureWidth()和getMeasureHeight()来决定的,所以在默认实现中,View的测量宽高和最终宽高是一样的即mRight - mLeft == mMeasureWidth & MEASURED_SIZE_MASK
但是两者赋值的时机不同,测量要早一点。某些特殊的情况下会导致两者不一致,比如重写View的layout方法或者多次measure时的前几次测量过程得到的测量宽高。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值