preface

In the performTraversals method, after the measure is executed, the layout process begins.

Source code analysis

Analysis of the entrance

In formTraversals there is a method:

// Execute layout methods after measure
performLayout(lp, mWidth, mHeight);
Copy the code
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight) {
    1. Start the layout process
    final View host = mView;
    host.layout(0.0, host.getMeasuredWidth(), host.getMeasuredHeight()); ...}Copy the code

1. Start the layout process

Public void layout(int l, int t, int r, int b) {int oldL = mLeft; int oldT = mTop; int oldB = mBottom; int oldR = mRight; SetFrame ()/setOpticalFrame()/setFrame()/setFrame() isLayoutModeOptical() boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); // If the size & position of the View changes // all the child views of the View are repositioned in the parent container: OnLayout () if (changed | | (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) = = PFLAG_LAYOUT_REQUIRED) {/ / The onLayout method has a difference between View and ViewGroup. The onLayout method has a difference between View and ViewGroup. The onLayout method has a difference between View and ViewGroup. OnLayout (changed, L, T, R, B); ...}Copy the code

IsLayoutModeOptical analysis

// Check whether it is a ViewGroup
public static boolean isLayoutModeOptical(Object o) {
    return o instanceof ViewGroup && ((ViewGroup) o).isLayoutModeOptical();
}
Copy the code

SetFrame analysis

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

    // If the layout position changes, perform the operation
    if(mLeft ! = left || mRight ! = right || mTop ! = top || mBottom ! = bottom) { changed =true;

        int drawn = mPrivateFlags & PFLAG_DRAWN;

        // Check whether the size has changed
        int oldWidth = mRight - mLeft;
        int oldHeight = mBottom - mTop;
        int newWidth = right - left;
        int newHeight = bottom - top;
        booleansizeChanged = (newWidth ! = oldWidth) || (newHeight ! = oldHeight);// View refresh
        invalidate(sizeChanged);

        // Synchronize layout location data
        mLeft = left;
        mTop = top;
        mRight = right;
        mBottom = bottom;
        mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);

        mPrivateFlags |= PFLAG_HAS_BOUNDS;
        
        // Call the size change method --> Additional analysis: size change
        if (sizeChanged) {
            sizeChange(newWidth, newHeight, oldWidth, oldHeight);
        }

        // The following size change operation
        if((mViewFlags & VISIBILITY_MASK) == VISIBLE || mGhostView ! =null) {
            
            mPrivateFlags |= PFLAG_DRAWN;
            invalidate(sizeChanged);
            
            invalidateParentCaches();
        }

        mPrivateFlags |= drawn;

        mBackgroundSizeChanged = true;
        mDefaultFocusHighlightSizeChanged = true;
        if(mForegroundInfo ! =null) {
            mForegroundInfo.mBoundsChanged = true;
        }

        notifySubtreeAccessibilityStateChangedIfNeeded();
    }
    return changed;
}
Copy the code

Additional analysis: dimensional variation

private void sizeChange(int newWidth, int newHeight, int oldWidth, int oldHeight) {
    // The onSizeChanged method is called, which can be overridden in a custom View to respond to size changesonSizeChanged(newWidth, newHeight, oldWidth, oldHeight); ...}Copy the code

SetOpticalFrame analysis

private boolean setOpticalFrame(int left, int top, int right, int bottom) {

    Insets parentInsets = mParent instanceof View ?
            ((View) mParent).getOpticalInsets() : Insets.NONE;

    Insets childInsets = getOpticalInsets();

    // Inside is actually calling setFrame ()
    return setFrame(
            left   + parentInsets.left - childInsets.left,
            top    + parentInsets.top  - childInsets.top,
            right  + parentInsets.left + childInsets.right,
            bottom + parentInsets.top  + childInsets.bottom);
}
Copy the code

Analysis 2. Respond to layout operations

View

Protected void onLayout(Boolean changed, int left, int top, int right, Int bottom) {// Parameter Description // changed Current View size and position changed // left left position // top right position // right position // bottom position}Copy the code

ViewGroup

Since onLayout in ViewGroup is an abstract method, it is calculated by subclass layout according to the logic. Here we use FrameLayout as an example:

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    // Layout the logic of the child controls
    layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
Copy the code

LayoutChildren source code analysis

void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
    // Get the number of child Views
    final int count = getChildCount();
    
    // Get the coordinates of the parent layout
    final int parentLeft = getPaddingLeftWithForeground();
    final int parentRight = right - left - getPaddingRightWithForeground();
    final int parentTop = getPaddingTopWithForeground();
    final int parentBottom = bottom - top - getPaddingBottomWithForeground();

    // Walk through the child View to calculate the position
    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;
            }

            // The child View calculates the positionchild.layout(childLeft, childTop, childLeft + width, childTop + height); }}}Copy the code

conclusion

View layout process generally can be divided into two kinds:

  • The View of the layout
  • The ViewGroup layout

The Layout procedure of a View only has a single Layout of its own position, whereas the Layout procedure of a ViewGroup needs to traverse the child View for the overall Layout.

In the daily open process, the general will overwrite custom View onLayout method:

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    // Get the actual width and height
}
Copy the code

You can also clone the Layout method if you want to customize it completely.

In addition, there is the extra extension onSizeChanged, which is called when the size changes.