The Working Principle of View: Basic Knowledge (1)

Keywords: Windows Android

ViewRoot and DecorView

ViewRoot

ViewRoot may be unfamiliar, but its role is significant. All View rendering and event distribution interactions are performed or passed through it.
ViewRoot corresponds to ViewRootImpl class, which is the link between Windows Manager and DecorView. The three main processes of View (measure ment, layout, draw ing) are all completed through ViewRoot.

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {

public final class ViewRoot extends Handler implements ViewParent,
 View.AttachInfo.Callbacks {

The following pictures are used directly for the convenience of notes. Come from: picture source

ViewRoot is not part of the View tree. From the source implementation point of view, it is neither a subclass of View nor a parent of View, but it implements the ViewParent interface, which makes it a parent view in the name of View.
RootView inherits the Handler class and can receive and distribute events. All touchscreen events, keystroke events, interface refresh events in Android are distributed through ViewRoot.
And when I tracked ViewRoot, I found that there was such a paragraph in the handleResumeActivity() method in ActivityThread:

// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
//ViewRoot usually sets callbacks using activities in addView - > ViewRootImpl # setView.
//If we do not reuse the decor view, we must notify the view root that the callback may have changed.
 ViewRootImpl impl = decor.getViewRootImpl();
 if (impl != null) {
     impl.notifyChildRebuilt();
 }

As you can see, decor can get (create) the ViewRootImpl object and notify the root view with impl object to make changes (establish associations).
Veiw's drawing process starts with the performance Traversals method of ViewRoot. It goes through three processes: measure, layout and draw before finally drawing a View.

See this article for more information. ViewRootImpl Source Analysis Event Distribution

DecorView

It's a top-level View. It's a FrameLayout itself. Generally, it contains a vertical line Layout inside. In this line Layout, there are two parts, the title bar above, the content bar below, and the id is content. Here's how we set up View in Activity: setContentView. It loads the layout into FrameLayout with id content.

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
How do I get this content?
ViewGroup content =(ViewGroup)findViewById(android.R.id.content). 
ViewGroup rootView = (ViewGroup) content.getChildAt(0);

More Reference link

MeasureSpec

Function: ("Measurement Specification", "Measurement Instructions")

It participates in the measurement process of View, and it largely determines the size of a View. Note that the size of View is also affected by its parent container, which affects the creation process of View MS. During the measurement process, the Layout Params of View will be transformed into the corresponding MS according to the rules imposed by the parent container, and then the width/height of View will be measured according to the MS. The width/height measured here is not necessarily equal to the final width/height.

introduce

MS stands for a 32-bit int value, high 2-bit stands for SpecMode, SpecMode refers to measurement mode, low 30-bit stands for SpecSize, SpecSize refers to the size of a certain measurement mode.

private static final int MODE_SHIFT = 30;
private static final int MODE_MASK  = 0x3 << MODE_SHIFT;

/** @hide */
@IntDef({UNSPECIFIED, EXACTLY, AT_MOST})
@Retention(RetentionPolicy.SOURCE)
public @interface MeasureSpecMode {}

/**
 * Measure specification mode: The parent has not imposed any constraint
 * on the child. It can be whatever size it wants.
 */
public static final int UNSPECIFIED = 0 << MODE_SHIFT;

/**
 * Measure specification mode: The parent has determined an exact size
 * for the child. The child is going to be given those bounds regardless
 * of how big it wants to be.
 */
public static final int EXACTLY     = 1 << MODE_SHIFT;

/**
 * Measure specification mode: The child can be as large as it wants up
 * to the specified size.
 */
public static final int AT_MOST     = 2 << MODE_SHIFT;

As can be seen from the above code, SpecMode and SpecSize are also int values. It is MS that packages SpecMode and SpecSize into an MS (that's all) to avoid excessive allocation of object memory, and MS provides unpacking methods to get the original two values.
Note: The MS mentioned here is that the int value represented by the value MS is not the MS itself.

SpecMode falls into three categories (the following explanation is similar to the English meaning of the source code above):
(1) UNSPECIFIED: The parent container does not have any restrictions on View, how big to give, which is generally used within the system to represent a measurement state.
(2) EXACTLY: The parent container has measured the exact size required by the View, at which point the final size of the View is the value specified by SpecSize. It corresponds to match_parent in LayoutParams and specific numerical values.
(3) AT_MOST: The parent container specifies a usable size, SpecSize, and the size of View should not be greater than this value, depending on the specific implementation of different Views. It corresponds to warp_content in LayoutParams.

Corresponding relationship between MeasureSpec and LayoutParams

As mentioned above, the system interior is measured by MS View, but under normal circumstances we specify MS with View. Nevertheless, we can set Layout Params for View. Because when the View is measured, the system will convert LayoutParams under the constraints of the parent container into the corresponding MS, and then determine the width/height of the View measurement based on the MS. Note: MS is not the only one determined by LayoutParams. LayoutParams needs to work with parent containers to determine MS and further determine the width/height of View.

The MS transformation of top-level View is slightly different from that of normal View.
- DecorView: MS is determined by the size of the window and its own Layout Params.
- Ordinary View: MS is determined by its parent container MS and its own Layout Params. Once the MS is determined, the measurement width/height of View can be determined in onMeasure.

For DecorView, the measureHierarchy method in ViewRootImpl has the following code:

childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);    //MS denotes wide
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); //MS is high  
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

It shows the creation of DecorView MS, where desir Windows Width and desired Windows Height are screen sizes.
Take another look at getRootMeasureSpec:

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.            
                break;        
    }        
                return measureSpec;    
}

According to the above code, the MS generation process of DecorView is clear and follows the following rules:

  • LayoutParams.MATCH_PARENT: Precise mode, size is window size
  • LayoutParams.WRAP_CONTNET: Maximum mode, variable size, but not beyond window size.
  • Fixed size (such as 100dp): Precise mode, size specified by LayoutParams.

    For ordinary View, the measurement process of View is passed from ViewGroup. First, take a look at the measureChildWithMargins method of ViewGroup:

 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); //Calling measure of child elements
    }

The above method calls the measure of the child element and gets the MS of the child element before calling the measure method of the child element. From the getChildMeasureSpec method, we can see that the MS of the child element is related to the MS, padding, margin of the parent container and LayoutParams of the child element.

Look specifically at how getChildMeasureSpec handles these factors and converts them into sub-elements of MS:

 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);
    }

Although the code is a bit long, it is not difficult to understand. First, the MS of the parent container is unpacked, and a specMode and a specSize are produced. The specMode is then passed in to calculate the specSize and specMode of the child element through a rule (switch statement), and then returns the MS of the child element through the last sentence "return MeasureSpec.makeMeasureSpec(resultSize, resultMode)". This is mainly based on the MS of the parent container and its own Layout Params to determine the MS of the child element. Padding in the parameter refers to the space used in the parent container, so the size of the child element is the size of the parent container minus Padding.

The following table summarizes the code:

Verification: For normal View, its MS is determined by the parent container's MS and its own Layout Params.
When View is fixed in width and height, the MS of View is precise, and the size is set for LayoutParams.
(2) When the width/height of the View is warp_content, the MS of the View is the maximum mode and the size is the size of the remaining space of the parent container.
(3) When the width/height of the View is match_parent, if the parent container is the exact mode, then the MS of the View is the exact mode and the size is the remainder of the parent container; if the parent container is the largest mode, then the MS of the View is the largest mode and the size is the remainder of the parent container.
(4) There is also UNSPECIFIED mode, but this mode is mainly used in the case of multiple measure ments within the system, so it is generally not concerned. So, as long as the MS of the parent container and LayoutParams of the child elements are provided, the MS of the child elements can be determined, and then the size of the child elements can be determined.

Posted by jimmy2gurpreet on Wed, 08 May 2019 01:00:40 -0700