Learning materials

  • Exploring the Art of Android Development
  • Blog.csdn.net/carson_ho/a…
  • www.jianshu.com/p/8766babc4…

Drawing process

The View is drawn from performTraversals() of ViewRoot, which displays the View on the screen through measure, layout and draw.

  • The measure process measures the width and height of the View.
  • The Layout process determines the position and final size of the View.
  • The Draw process draws the View on the screen.

These three processes are described in turn.

Measure Measurement process

MeasureSpec is how the system measures the View, and it’s important to know about MeasureSpec before you can understand the measurement process.

MeasureSpec

MeasureSpec is a 32-bit int value packaged as MeasureSpec to avoid excessive object memory allocation.

For ease of operation, MeasureSpec provides a quick and easy way to package and unpack.

  • MeasureSpec.makeMeasureSpec( int size, int mode)
  • MeasureSpec.getMode(int measureSpec)
  • MeasureSpec.getSize(int measureSpec)

MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec

There are three measurement modes in MeasureSpec

  • UNSPECIFIED dimensions are left to the View.
  • EXACTLY exact mode, in which the following specSize is used, generally corresponds to LayoutParams’ match_content and the exact size set.
  • AT_MOST Maximum mode. In this mode, the maximum size of a view cannot exceed the following specSize. This mode generally corresponds to wrAP_content of LayoutParams

When measuring a View, the system converts its LayoutParams parameter to its own MeasureSpec under the influence of the parent container’s MeasureSpec, and then measures its own width and height using that MeasureSpec.

Note that the View’s MeasureSpec is not uniquely determined by LayoutParams, but is created under the combined influence of the parent container.

MeasureChild () is the measureChild of the ViewGroup. GetChildMeasureSpec () converts layoutParams to measureSpec.


protected void measureChild(View child, int parentWidthMeasureSpec,
        int parentHeightMeasureSpec) {
        // Get the LayoutParams parameter of the child element
    final LayoutParams lp = child.getLayoutParams();

    // Create the child's measureSpec
    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
            mPaddingLeft + mPaddingRight, lp.width);
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
            mPaddingTop + mPaddingBottom, lp.height);

    // Pass the measurement to the child element
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    // Parse the parent container's measureSpec, resolving the schema and size
    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) {
    // The parent container is the exact mode case, set the exact size.
    case MeasureSpec.EXACTLY:
        if (childDimension >= 0) {
        // The child element itself is the exact size set, i.e. EXACTLY mode, and the size is the exact size set.
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // The match_content of the child element fills the container, so the size is set to the size of the container and the mode is set to EXACTLY
            resultSize = size;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            In wrap mode, child elements can set their own size, but cannot exceed the size of the parent container. The mode is AT_MOST and the size is the size of the parent container.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;

    // The parent container is the maximum mode
    case MeasureSpec.AT_MOST:
        if (childDimension >= 0) {
            // Set to the size of the child element, in exact mode
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // If the child element wants to fill the parent container, it should be set to the size of the parent container, but the parent container is in maximum mode and there is no exact size.
            // So set the child element to the maximum mode, which cannot exceed the current size of the parent container.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // The child element has no exact size and wants to wrap itself. In this mode, set it to the maximum size, as long as it does not exceed the parent container size.
            // bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;

    // There is no limit to the parent container
    case MeasureSpec.UNSPECIFIED:
        if (childDimension >= 0) {
            // The child element has its own set value, so it can use its own value, set to exact mode
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // If the child element wants to fill the parent, it finds the dimensions of the parent, but the dimensions of the parent are unknown, so it still has to be UNSPECIFIED.
            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Only the elements are wrapped with UNSPECIFIED values. The parent container has no reference, so the child elements are left UNSPECIFIED
            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        }
        break;
    }
    // Use the wrap method to wrap and return the pattern and size of the child elements
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
Copy the code

The measure process starts in View wroot’s performMeasure().

Here you convert the DecorView’s layoutParams to your Own measureSpec under the influence of the Window’s measureSpec. The measure() of the DecorView is then called to pass in the measureSpec. In measure(), the DecorView begins its own measurement.

From the DecorView measure(), the entire View tree measurement process begins.

MeasureSpec: measureSpec: measureSpec: measureSpec: measureSpec: measureSpec: measureSpec: measureSpec: measureSpec: measureSpec: measureSpec: measureSpec: measureSpec: measureSpec: measureSpec: measureSpec: measureSpec: measureSpec: measureSpec: measureSpec: Measure() The final measurement is done in onMeasure().

Usually we custom View to rewrite the method to achieve their own measurement logic, including our commonly used controls are their own rewrite the method to achieve their own measurement logic.

If onMeasure() is not overridden, the wrap_content parameter of the custom view will be invalid. See the getDefaultSize() implementation for details.


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

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:
        SpecSize is used for both precise and Max modes, which invalidates wrAP_content and always fills the parent container.
        result = specSize;
        break;
    }
    return result;
}

protected int getSuggestedMinimumHeight(a) {
    return (mBackground == null)? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight()); }protected int getSuggestedMinimumWidth(a) {
    return (mBackground == null)? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth()); }Copy the code

View and ViewGroup are measured differently.

A pure View only needs to perform its own measurements in onMeasure(). A ViewGroup can perform its own measurements as well as its child elements.

There is no implementation of onMeasure() in ViewGroup, because each layout has different characteristics and the specific measurement logic is also different, and the specific implementation is in each layout.

MeasureChildren () ¶ The measureChildren() method is a measureChildren() method that iterates through all the child elements and takes their LayoutParams parameter and creates the child’s measureSpec under the influence of its own measureSpec. MeasureSpec is then passed in by calling measure() of the child element.

This is passing the measurement to the child element. If the child element is a pure View control, it just completes itself. If the child element is a ViewGroup, the measurement will continue to be recursive until the entire View tree has been measured.

protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { final int size = mChildrenCount; final View[] children = mChildren; for (int i = 0; i < size; ++i) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) ! = GONE) {// measureChild, measureChild see MeasureSpec above. measureChild(child, widthMeasureSpec, heightMeasureSpec); }}}Copy the code

After completing the measurement process, we will enter the layout process.

Layout Process

The Layout process determines the position of the View’s four vertices, which in turn determines their position in the parent container and the final width and height.

The layout process is also started in the ViewRoot. It is in performLayout() that the DecorView’s Layout() method is first called to start the layout process for the entire View tree.

The View layout process is done in the Layout () method, which sets the position of its four vertices with setFrame().

After setting its position, the onLayout() method is called. If it is a ViewGroup, the onLayout method can be used to determine the position of the child elements in the onLayout.

View onLayout() is not implemented because it has no child elements, and ViewGroup itself is not implemented, which is implemented in the specific layout itself.

The idea is also to traverse all the sub-elements that need to be laid out. After calculating their positions according to the measured size, the layout() method of the sub-elements is called to thread in the position parameters, so that the sub-elements can complete their layout process.

In this case, the layout flow is passed to the child element, and if the child element is a ViewGroup, the layout flow will continue until the entire View tree layout flow is completed.

  • Layout () determines its position
  • OnLayout () determines the location of child elements

After completing the layout process, it is the last DRAW process.

Draw drawing process

The process is to draw the View onto the screen.

The draw process also starts in ViewRoot, specifically in performDraw(), where the DecorView’s draw() is called to begin drawing the entire View tree.

The process of draw is relatively simple, and you can see the whole step in draw()

  1. DrawBackground (canvas);
  2. Draw your own content onDraw(Canvas);
  3. Draws a child element dispatchDraw(Canvas);
  4. OnDrawForeground (canvas);

Our custom View will implement its own drawing logic in onDraw(), View dispatchDraw() is not implemented, the specific implementation is in the ViewGroup.

Call the child’s draw() after the ViewGroup to pass the drawing process to the child until the entire View tree is drawn.

After the entire View tree is drawn, you can see the interface on the screen.

Related classes & concepts

In View drawing process, involved in a lot of classes, here will not do a detailed introduction, just a simple list here, know the role of these.

DecorView

The root node of the whole View tree, all the drawing, all the events are distributed from this View.

It inherits FrameLayout, which is a ViewGroup with a LinearLayout inside.

The LinearLayout has a FrameLayout with a CONTENT id. The setContentView() that we normally set is loaded into this FrameLayout.

Window

Every Activity has a window, which is a member variable of the Activity and the view window of the application, hosting the view of the entire Activity. It contains a DeocrView member variable that hosts the view.

It currently has only one implementation class, PhoneWindow, an instance of which is mWindow in the Activity.

ViewRoot

The View Root is useful as the link between the DecorView and Window Manager. View drawing, touch screen, keystroke, screen refresh and other events are distributed through it.

Activity view structure

End

I hope you can leave a comment in the comments section.