绘制流程入口源码分析中知道了 performTraversals()中通过调用 performLayout(), 然后进行了 UI控件的布局 。

View布局摆放

performTraversals的测量被调用之后,会再次调用performLayout开始具体布局的摆放。

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
                           int desiredWindowHeight) {
    // ...
    // host 就是decorView
    final View host = mView;
    if (host == null) {
        return;
    }
    // ..
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
    try {
        // 调用view的layout
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
        // ...
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    mInLayout = false;
}

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进行初始化mLeft,mRight,mTop,mBottom这四个值
    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);
 
        // ...
    }
    // ...
}

layout最主要做的一件事情就是对我们的上下左右四个点进行初始化。

View#setFrame

protected boolean setFrame(int left, int top, int right, int bottom) {
    boolean changed = false;
 
    // ...
 
    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;
 
 
        if (sizeChanged) {
            sizeChange(newWidth, newHeight, oldWidth, oldHeight);
        }
 
        if ((mViewFlags & VISIBILITY_MASK) == VISIBLE || mGhostView != null) {
            // If we are visible, force the DRAWN bit to on so that
            // this invalidate will go through (at least to our parent).
            // This is because someone may have invalidated this view
            // before this call to setFrame came in, thereby clearing
            // the DRAWN bit.
            mPrivateFlags |= PFLAG_DRAWN;
            invalidate(sizeChanged);
            // parent display list may need to be recreated based on a change in the bounds
            // of any child
            invalidateParentCaches();
        }
 
        // Reset drawn bit to original value (invalidate turns it off)
        mPrivateFlags |= drawn;
 
        mBackgroundSizeChanged = true;
        mDefaultFocusHighlightSizeChanged = true;
        if (mForegroundInfo != null) {
            mForegroundInfo.mBoundsChanged = true;
        }
 
        notifySubtreeAccessibilityStateChangedIfNeeded();
    }
    return changed;
}

setFrame在进行初始化的时候会对比上一次是否一致,若一致则不会在此进行,若是一致,则会使我们旧的信息直接失效invalidate(sizeChanged)

View#onLayout

  /**
* Called from layout when this view should
* assign a size and position to each of its children.
*
* Derived classes with children should override
* this method and call layout on each of
* their children.
* @param changed This is a new size or position for this view
* @param left Left position, relative to parent
* @param top Top position, relative to parent
* @param right Right position, relative to parent
* @param bottom Bottom position, relative to parent
*/
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}

发现View的onLayout()并没有做任何的事儿,从上述的分析中知道我们是调用的DecorView的layout方法。

DecorView#layout

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

这时发现他调用了parent(FrameLayout)onLayout

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}

所以布局流程与测量流程一样,每个布局的实现方式都是不一样的,都是根据自定义view时的业务流程进行布局摆放。布局摆放流程实际上是,先得到顶层, 顶层自己先开始layout进行布局定位,然后调用onLayout调用子view让子view调用自己的layout对自己进行定位以达到定位的所有目的。