Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

An overview of

1.1 Android View inheritance diagram

View is the parent class of all Android controls.

1.2 View coordinates of Android

1.2.1 Android Coordinate system

GetRawX () and getRawY() provided by MotionEvent;

1.2.2 View Coordinate system

1.2.3 Common methods

View Gets its width and height

getHeight() Gets the height of the View itself
getWidth() Gets the View’s own width

Distance between View and its parent (ViewGroup) :

getTop() Gets the distance from the top edge of the View itself to the top edge of its parent layout
getLeft() Gets the distance from the left of the View itself to the left of its parent layout
getRight() Gets the distance from the right of the View itself to the left of its parent layout
getBottom() Gets the distance from the bottom edge of the View itself to the top edge of its parent layout

Method provided by MotionEvent

So if we look at that dark blue dot in the figure above, let’s say that’s the point we touch, and we know that the final click event, whether it’s a View or a ViewGroup, is going to be handled by onTouchEvent, MotionEvent also provides various methods for getting focal coordinates:

getX() Gets the distance from the click event to the left of the control, the view coordinates
getY() Gets the distance of the click event from the top edge of the control, the view coordinates
getRawX() Gets the absolute coordinates of how far the click event is to the left of the entire screen
getRawY() Take the distance between the click event and the top edge of the entire screen, that is, the absolute coordinates

2. View event distribution

Click event distribution: Clicking on the screen produces a touch event encapsulated in a class: MotionEvent. When this MotionEvent is generated, the system will send this MotionEvent to all levels of the View. The process of MotionEvent transmission at the View level is click event distribution.

There are three important ways to click on events:

dispatchTouchEvent(MotionEvent ev) Used for event distribution
onInterceptTouchEvent(MotionEvent ev) For event interception, called from dispatchTouchEvent(). Note that this method is not provided by the View
onTouchEvent(MotionEvent ev) Used to handle click events, called in the dispatchTouchEvent() method

The relationship between these three important methods of click event distribution is simply expressed in pseudocode:

 public boolean dispatchTouchEvent(MotionEvent ev) {
 boolean result=false;
 if(onInterceptTouchEvent(ev)){
       result=super.onTouchEvent(ev);
  }else{
       result=child.dispatchTouchEvent(ev);
 }
 return result;
Copy the code

This U-shaped diagram is a good example of event distribution:

Summary: dispatchTouchEvent represents the dispatch event, onInterceptTouchEvent() represents the intercept event, onTouchEvent() represents the consumable event, which is handled by itself.

3. Work flow of View

3.1 an overview of the

Measure: measure the width and height of the View; Measurement;

Layout: Used to determine the position of the View; Layout;

Draw: is used to draw a View; Drawing;

3.2 measurement

MeasureSpec (important)

MeasureSpec is the static inner class of a View; MeasureSpec MeasureSpec MeasureSpec MeasureSpec MeasureSpec MeasureSpec MeasureSpec MeasureSpec

The measurement process is essentially the process of determining the View’s MeasureSpec, which contains two main parameters: mode + size.

MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec

3.2.2 SpecMode Classification of MeasureSpec

UNSPECIFIED

public static final int UNSPECIFIED = 0 << MODE_SHIFT; 00000000000000000000000000000000

The parent container does not impose any restrictions on the View, and is used internally

EXACTLY public static final int EXACTLY = 1 << MODE_SHIFT; 01000000000000000000000000000000 the parent container of measuring the size of the View, the size of the Vew is SpecSize LayoutPamras match_parent fixed size

AT_MOST public static final int AT_MOST = 2 << MODE_SHIFT; 10000000000000000000000000000000 the parent container to specify a size are available, and the size of the View must not exceed this value, LayoutPamras wrap_content

3.2.3 Mapping process

ViewGroup mapping process:

Measure –> onMeasure –>setMeasuredDimension –>setMeasuredDimensionRaw

View mapping process:

Measure –> onMeasure –>setMeasuredDimension –>setMeasuredDimensionRaw

3.2.4 Mapping source code analysis, using DecorView as an example

First in view otimPL, execute the performMeasure method:

 private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        if (mView == null) {
            return;
        }
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally{ Trace.traceEnd(Trace.TRACE_TAG_VIEW); }}Copy the code

Click the mview. measure method to enter the View’s measure method (marked 1) (the method of View is final and cannot be overridden) as follows:

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    boolean optical = isLayoutModeOptical(this);
    if(optical ! = isLayoutModeOptical(mParent)) { Insets insets = getOpticalInsets();int oWidth  = insets.left + insets.right;
        int oHeight = insets.top  + insets.bottom;
        widthMeasureSpec  = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth);
        heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
    }
 
    // Suppress sign extension for the low bytes
    long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
    if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);
 
    final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
 
    // Optimize layout by avoiding an extra EXACTLY pass when the view is
    // already measured as the correct size. In API 23 and below, this
    // extra pass is required to make LinearLayout re-distribute weight.
    final booleanspecChanged = widthMeasureSpec ! = mOldWidthMeasureSpec || heightMeasureSpec ! = mOldHeightMeasureSpec;final boolean isSpecExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY
            && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY;
    final boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec)
            && getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec);
    final booleanneedsLayout = specChanged && (sAlwaysRemeasureExactly || ! isSpecExactly || ! matchesSpecSize);if (forceLayout || needsLayout) {
        // first clears the measured dimension flag
        mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
 
        resolveRtlPropertiesIfNeeded();
 
        int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
        if (cacheIndex < 0 || sIgnoreMeasureCache) {
            // measure ourselves, this should set the measured dimension flag back
            / / here! The important onMeasure method finally finds you!
            / / here! The important onMeasure method finally finds you!
            / / here! The important onMeasure method finally finds you!
 
            onMeasure(widthMeasureSpec, heightMeasureSpec);
            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        } else {
            long value = mMeasureCache.valueAt(cacheIndex);
            // Casting a long to int drops the high 32 bits, no mask needed
            setMeasuredDimensionRaw((int) (value >> 32), (int) value);
            mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        }
 
        // flag not set, setMeasuredDimension() was not invoked, we raise
        // an exception to warn the developer
        if((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) ! = PFLAG_MEASURED_DIMENSION_SET) {throw new IllegalStateException("View with id " + getId() + ":"
                    + getClass().getName() + "#onMeasure() did not set the"
                    + " measured dimension by calling"
                    + " setMeasuredDimension()");
        }
 
        mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
    }
 
    mOldWidthMeasureSpec = widthMeasureSpec;
    mOldHeightMeasureSpec = heightMeasureSpec;
 
    mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
            (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
}
Copy the code

There’s a lot of code. Don’t look at it all. The first half is basically fetching the cache, and then executing the onMeasure(widthMeasureSpec, HeightMeasure) method. A setting that will be cached if the cache is not read. (The cache can be ignored for now, mainly to do some optimization for the measurement)

Next in the code snippet above, click on the main onMeasure method to check it out:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
Copy the code

The onMeasure method calls two main methods: setMeasuredDimension and getDefaultSize. Let’s start with the getDefaultSize method:

 public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
 
        switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }
Copy the code

Note that either AT_MOST or EXACTLY mode is the default specSize, so we must override the onMeasure method when we customize the View, otherwise match_parent and wrap_content properties will not work.

Let’s take a look at what’s in the setMeasuredDimension method:

protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
    boolean optical = isLayoutModeOptical(this);
    if(optical ! = isLayoutModeOptical(mParent)) { Insets insets = getOpticalInsets();int opticalWidth  = insets.left + insets.right;
        int opticalHeight = insets.top  + insets.bottom;
 
        measuredWidth  += optical ? opticalWidth  : -opticalWidth;
        measuredHeight += optical ? opticalHeight : -opticalHeight;
    }
    setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
Copy the code

The setMeasuredDimension method calls the setMeasuredDimensionRaw(measuredWidth, measuredHeight) method.

private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
    mMeasuredWidth = measuredWidth;
    mMeasuredHeight = measuredHeight;
 
    mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
Copy the code

Thank god it’s over! As you can see, the method assigns a value to the width and height member variable and sets a marker bit.

Don’t walk so far that you forget why you started. Measure (childWidthMeasureSpec, childHeightMeasureSpec) keeps coming back here by executing the mView. Measure (performMeasure) method in ViewRootImpl, So measure is essentially determining the width and height of the control. How do the width and height of the controls determine their values? Now we need to look at mView.measure(childWidthMeasureSpec, childHeightMeasureSpec). ChildHeightMeasureSpec these two parameters!

Here is a line for everyone to understand, above is the source code process analysis of mview.measure method.


Next we need to know how to get the two parameters of the mview.measure method.

Continue reading the source code in ViewRootlmpl:

                int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
                int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
 
                if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed! mWidth="
                        + mWidth + " measuredWidth=" + host.getMeasuredWidth()
                        + " mHeight=" + mHeight
                        + " measuredHeight=" + host.getMeasuredHeight()
                        + " coveredInsetsChanged=" + contentInsetsChanged);
 
                 // Ask host how big it wants to be
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
Copy the code

As you can see, GetRootMeasureSpec (mWidth, lp.width) and getRootMeasureSpec(mHeight, lp.width). Lp. Height). In the first case, mWidth represents the width of the PhoneWindow, and the second argument represents the width of the top-level View itself. GetRootMeasureSpec = getRootMeasureSpec = getRootMeasureSpec = getRootMeasureSpec = getRootMeasureSpec

/**
 * Figures out the measure spec for the root view in a window based on it's
 * layout params.
 *
 * @param windowSize
 *            The available width or height of the window
 *
 * @param rootDimension
 *            The layout params for one dimension (width or height) of the
 *            window.
 *
 * @return The measure spec to use to measure the root view.
 */
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
    int measureSpec;
    switch (rootDimension) {
 
    case ViewGroup.LayoutParams.MATCH_PARENT:
        // Window can't resize. Force root view to be windowSize.
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
        break;
    case ViewGroup.LayoutParams.WRAP_CONTENT:
        // Window can resize. Set max size for root view.
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
        break;
    default:
        // Window wants to be an exact size. Force root view to be that size.
        measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
        break;
    }
    return measureSpec;
}
Copy the code

MeasureSpec (match_parent/wrAP_content) measureSpec (EXACTLY/AT_MOST) = measureSpec (EXACTLY/AT_MOST) = match_parent (EXACTLY/AT_MOST);

The DecorView’s MeasureSpec method is determined by the window size and its own LayoutParams. The specific decision rule logic in the above code can be summarized as follows:

Keep analyzing. We just determined where these two parameters came from. In fact, these two parameters determine the mode and size of the DccorView (note that the measurement of the child view has not been done yet, the process is just beginning).

Here are two arguments to the mView.measure method to avoid confusion.


Let’s move on to the mView.measure method.

Call the onMeasure method in mView.measure. We know that the DecorView actually integrates with FrameLayout, so let’s go directly to FrameLayout and see the implementation of the onMeasure method in action:

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

    final booleanmeasureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) ! = MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) ! = MeasureSpec.EXACTLY; mMatchParentChildren.clear();int maxHeight = 0;
    int maxWidth = 0;
    int childState = 0;
 
    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); }}}}// 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++) {
            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); }}}Copy the code

FrameLayout, as a container, must contain many child views. As you can see from the above source code method, it also traverses all its child views. It is not difficult to imagine that it will measure all its child views. To verify the conjecture, click on MeasureChildWithNavigation to find out more:

protected void measureChildWithMargins(View child,
        int parentWidthMeasureSpec, int widthUsed,
        int parentHeightMeasureSpec, int heightUsed) {
    final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
 
    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
            mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                    + widthUsed, lp.width);
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
            mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                    + heightUsed, lp.height);
 
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
Copy the code

In this method, the padding is added to the processing, which is easy to understand without further elaboration.

Look at the child.measure(childWidthMeasureSpec, ChildHeightMeasure) method. As you can see from the name, the width and height of the child View in FrameLayout is measured.

Here’s a very important point: the getChildMeasureSpec method, which is a child View’s MeasureSpec. Click to see the detailed implementation of the getChildMeasureSpec method:

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    int specMode = MeasureSpec.getMode(spec);
    int specSize = MeasureSpec.getSize(spec);
 
    int size = Math.max(0, specSize - padding);
 
    int resultSize = 0;
    int resultMode = 0;
 
    switch (specMode) {
    // Parent has imposed an exact size on us
    case MeasureSpec.EXACTLY:
        if (childDimension >= 0) {
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size. So be it.
            resultSize = size;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size. It can't be
            // bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;
 
    // Parent has imposed a maximum size on us
    case MeasureSpec.AT_MOST:
        if (childDimension >= 0) {
            // Child wants a specific size... so be it
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size, but our size is not fixed.
            // Constrain child to not be bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size. It can't be
            // bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;
 
    // Parent asked to see how big we want to be
    case MeasureSpec.UNSPECIFIED:
        if (childDimension >= 0) {
            // Child wants a specific size... let him have it
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size... find out how big it should
            // be
            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size.... find out how
            // big it should be
            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        }
        break;
    }
    //noinspection ResourceType
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
Copy the code

This method is a bit long, so don’t be afraid. As you can see, the first argument to this method represents the parent’s MeasureSpec value (the parent’s mode and size), the second argument, the padding, and the third argument represent the size of the view itself. The method determines the mode and size of the child View by judging the mode of the parent container. The specific judgment logic can be seen from the above code, which is not detailed and summarized as follows:

After all the child controls are measured, FrameLayout starts to finalize its width and height. Continue looking at the onMeasure method in FrameLayout, and look at the second half of it. I’ll paste the key code again:

      // 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++) {
                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); }}Copy the code

As you can see, FrameLayout finds the height of the bottommost child and the height of the rightmost child of all child controls. From this we finally determined the width and height of Framelayout!

3.3 the layout

Source code always looks laborious, first summarized as follows:

The View:

Call the layout to determine their own position, namely to determine mLeft, mTop, mRight, mBottom value, obtain the location of the four points.

ViewGroup:

Call view. layout to determine its position, and call onLayout to determine the position of the child views.

3.3.1 Layout source code analysis, take FrameLayout as an example

View FrameLayout’s onLayout as follows:

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

Click to view the layoutChildren method:

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; } child.layout(childLeft, childTop, childLeft + width, childTop + height); }}}Copy the code

As you can see, the layoutChildren method iterates through the child View and positions the child controls. And the placement of the child control, the call is the child control layout method, that is, the last line of the code. The layout is that simple.

3.4 draw

ViewGroup drawing steps:

DrawBackground (Canvas)

Draw yourself onDraw(Canvas)

Draw sub-view dispatchDraw(Canvas)

Paint foreground, scroll bar, etc. onDrawForeground(canvas)

View drawing steps:

DrawBackground (Canvas)

Draw yourself onDraw(Canvas)

Paint foreground, scroll bar, etc. onDrawForeground(canvas)

Steps to draw a custom control:

OnMeasure –> onLayout –> onMeasure –> onLayout –> onMeasure –> onLayout –> onMeasure –> onLayout

3.4.1 Draw source code analysis, taking FrameLayout as an example

Find the performDraw method in ViewRootlmpl:

private void performDraw(a) {
        if(mAttachInfo.mDisplayState == Display.STATE_OFF && ! mReportNextDraw) {return;
        } else if (mView == null) {
            return;
        }
 
        final boolean fullRedrawNeeded = mFullRedrawNeeded || mReportNextDraw;
        mFullRedrawNeeded = false;
 
        mIsDrawing = true;
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
 
        boolean usingAsyncReport = false;
        if(mReportNextDraw && mAttachInfo.mThreadedRenderer ! =null
                && mAttachInfo.mThreadedRenderer.isEnabled()) {
            usingAsyncReport = true;
            mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
                // TODO: Use the frame number
                pendingDrawFinished();
            });
        }
 
        try {
            boolean canUseAsync = draw(fullRedrawNeeded);
            if(usingAsyncReport && ! canUseAsync) { mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
                usingAsyncReport = false; }}finally {
            mIsDrawing = false;
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
 
        // For whatever reason we didn't create a HardwareRenderer, end any
        // hardware animations that are now dangling
        if(mAttachInfo.mPendingAnimatingRenderNodes ! =null) {
            final int count = mAttachInfo.mPendingAnimatingRenderNodes.size();
            for (int i = 0; i < count; i++) {
                mAttachInfo.mPendingAnimatingRenderNodes.get(i).endAllAnimators();
            }
            mAttachInfo.mPendingAnimatingRenderNodes.clear();
        }
 
        if (mReportNextDraw) {
            mReportNextDraw = false;
 
            // if we're using multi-thread renderer, wait for the window frame draws
            if(mWindowDrawCountDown ! =null) {
                try {
                    mWindowDrawCountDown.await();
                } catch (InterruptedException e) {
                    Log.e(mTag, "Window redraw count down interrupted!");
                }
                mWindowDrawCountDown = null;
            }
 
            if(mAttachInfo.mThreadedRenderer ! =null) {
                mAttachInfo.mThreadedRenderer.setStopped(mStopped);
            }
 
            if (LOCAL_LOGV) {
                Log.v(mTag, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
            }
 
            if(mSurfaceHolder ! =null && mSurface.isValid()) {
                SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::postDrawFinished);
                SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
 
                sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
            } else if(! usingAsyncReport) {if(mAttachInfo.mThreadedRenderer ! =null) { mAttachInfo.mThreadedRenderer.fence(); } pendingDrawFinished(); }}}Copy the code

Code a lot don’t have to look, find the draw method, points in the draw () method in the code is very much also, not paste, find the draw () method in drawSoftware method, and then is the mView drawSoftware method. The draw () method. Click to enter the View’s draw() method as follows:

   public void draw(Canvas canvas) {
        final int privateFlags = mPrivateFlags;
        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null| |! mAttachInfo.mIgnoreDirtyState); mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;/* * Draw traversal performs several drawing steps which must be executed * in the appropriate order: * * 1. Draw the background * 2. If necessary, save the canvas' layers to prepare for fading * 3. Draw view's content * 4. Draw children * 5. If necessary, draw the fading edges and restore layers * 6. Draw decorations (scrollbars for instance) */
 
        // Step 1, draw the background, if needed
        int saveCount;
 
        if(! dirtyOpaque) { drawBackground(canvas); }// skip step 2 & 5 if possible (common case). .Copy the code

As you can see, the comments are written in great detail, listing all the steps to draw. The omitted and unpasted code is the specific code logic for executing the steps step by step.

Finally, a god diagram:

Refer to the article

The View of the smooth movement: blog.csdn.net/itachi85/ar…

Android View system: blog.csdn.net/itachi85/ar…

Juejin. Im /post/5d3e52…

The View of workflow: blog.csdn.net/qian520ao/a…