preface

We analyze the process from application click to activity. onCreate in the order of source code execution, but the user is still in the state of not seeing the page, so what actions will let the user to see our page? Therefore, this article is mainly to continue the analysis of the previous article. Generally speaking, an Activity is used to display and process the UI interface. However, the Activity does not directly manage the ViewTree, but is managed by the Window. In order to make the page in the display state, first build the relevant system of the Window, and then create the ViewTree. Then process the View in the ViewTree in turn.

1. Drawing the pre-work

1. Create the Window

PhoneWindow is a concrete implementation of a Window, an abstract concept that sits at the top of the page and provides an area where you can draw the UI and respond to input events.

Activity.java final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) { ... MWindow = new PhoneWindow(this, window, activityConfigCallback); . // setWindowManager mwindow.setwindowmanager ((windowManager) context.getsystemservice (context.window_service), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! = 0); . }Copy the code

PhoneWindow is created in Activity. Attach, so each Activity has a corresponding window. However, if so many Windows have to communicate with WMS one by one, it can waste resources and cause problems. So they are uniformly managed by WindowManager, which is an interface whose implementation is WindowManagerImpl.

2. Create a DecorView

DecorView is a View inherited from FrameLayout. It is the original container for the entire View, which contains a LinearLayout with two child elements, TitleView and ContentView. Normally in onCreate, setContentView is just parsing the XML layout and filling it in.

PhoneWindow.java @Override public void setContentView(int layoutResID) { ... If (mContentParent == null) {installDecor(); } else if (! hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); }... // Parses the XML file to inflate the View mlayoutInflater.inflate (layoutResID, mContentParent); . } private void installDecor() { ... If (mDecor == null) {//generateDecor = generateDecor(-1); . }... If (mContentParent == null) {// FindViewById (R.I.D.C.) findContentParent = generateLayout(mDecor); . }}Copy the code

The handleLaunchActivity method calls the Activity.onCreate method, and from that method calls setContentView, loading our layout into the DecorView.

3. Associate DecorView with ViewRootImpl

The ViewTree is a hierarchy of views that need to be attached to the Window to display, and it also needs to be managed, so the ViewRoot connects the DecorView to the WindowManager and manages the View’s measurement, layout, drawing, touching and other related events.

ActivityThread.java @Override public void handleResumeActivity(IBinder token, boolean finalStateRequest, Boolean onResume final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); if (r.window == null && ! // getWindow and DecorWindow r.wakedow = r.wake.getwindow (); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); // Get WindowManager ViewManager wm = a.getwinDowManager (); if (a.mVisibleFromClient) { if (! a.mWindowAdded) { a.mWindowAdded = true; Wm. addView(decor, l); } } } } WindowManager.java public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow, int userId) {synchronized (mLock) {// create ViewRootImpl root = new ViewRootImpl(view.getContext(), display); . Root.setview (view, wparams, panelParentView, userId); }}Copy the code

The above overall relationship creation and association flow chart is as follows, the students who need to view the source code:

2. The overall process of drawing

The drawing process starts from performMeasure, performLayout, and performDraw. They correspond to measure, Layout, and draw. Measure is used to calculate the area size of the view on the UI interface, Layout is used to calculate the position of the view, and draw is used to draw the content of the view.

ViewRootImpl.java private void performTraversals() { ... Int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); . PerformMeasure (childWidthMeasureSpec, childHeightMeasureSpec); . PerformLayout (lp, desiredWindowWidth, desiredWindowHeight); . PerformDraw (); }Copy the code

1. The measure process

1.1. Measurement rules

The getRootMeasureSpec method of the performTraversals method obtains the root view measure rule from the traversals method. All views must follow the same rule during the measure phase. This is based on the parent view’s MeasureSpec and its own LayoutParams, but there are exceptions, such as decorViews that are based on the window size and its own LayoutParams. LayoutParams specifies parameters such as the height and width of the view. It can be used in three ways:

  1. For example, 10DP
  2. MATCH_PARENT means that the child container wants to be the same size as the parent container
  3. WRAP_CONTENT means that the container can wrap its contents

MeasureSpec has three modes:

  1. The final size of the view is this value, which corresponds to the MATCH_PARENT and the actual value of the LayoutParams
  2. The AT_MOST parent container gives the size available. Views cannot exceed this value, which corresponds to the WRAP_CONTENT of LayoutParams
  3. The parent container has no restrictions on its children

Here’s an example with the source code:

private static int getRootMeasureSpec(int windowSize, int rootDimension) { int measureSpec; Switch (rootDimension) {/ / LayoutParams MATCH_PARENT corresponding EXACTLY model case of MeasureSpec ViewGroup. LayoutParams. MATCH_PARENT: measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; / / LayoutParams. WRAP_CONTENT corresponding MeasureSpec AT_MOST mode case ViewGroup. LayoutParams. WRAP_CONTENT: measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); break; Default: / / this kind of circumstance is to fill out the rules of a specific value to form a direct measureSpec with specific value = measureSpec. MakeMeasureSpec (rootDimension, measureSpec. EXACTLY); break; } return measureSpec; } public static int makeMeasureSpec(@intrange (from = 0, @intrange); to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,@MeasureSpecMode int mode) { if (sUseBrokenMakeMeasureSpec) { return size + mode; } else { return (size & ~MODE_MASK) | (mode & MODE_MASK); }}Copy the code

Figure out how MeasureSpec is built by matching the three modes of the LayoutParams with the rootDimension(the width/height information of the view).

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { ... //mView(DecorView) starts to measure mview. measure(childWidthMeasureSpec, childHeightMeasureSpec); . }Copy the code
1.2. The View of measurement

The onMeasure method of View will be called in the measure method of View. The onMeasure method of View is shown below.

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

SetMeasuredDimension sets the width and height of the View and must be set in onMeasure. Next, look at getDefaultSize

    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

In this method, the size of the View is determined by the specSize of the MesureSpec passed by the parent class, and the value of AT_MOST is the same as that of EXACTLY, so wrap_content loses its effect. So if you directly inherit from the view, override the onMeasure method to determine the wrAP_content mode and add the default value.

1.3. Measurement of ViewGroup

For DecorView, the actual measure is FrameLayout’s onMeasure. But before we look at FrameLayout’s onMeasure, let’s look at the VIewGroup measure. VIewGroup is an abstract class. The onMeasure is not implemented (because each subclass implements it differently, such as LinearLayout and RelativeLayout), but it is implemented by the subclass, but it does provide measureChildren, as follows:

ViewGroup.java 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(child, widthMeasureSpec, heightMeasureSpec); } } } protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { final LayoutParams lp = child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }Copy the code

MeasureChild () measureChild () measureChild () measureChild () measureChild () measureChild () measureChild () measureChild () measureChild () measureChild () measureChild So the whole system goes through the process of measurement. Take a look at FrameLayout’s onMeasure:

FrameLayout.java @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { ... for (int i = 0; i < count; I ++) {// loop all child views 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()); . }}... Setmeasuredimension (maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, widthMeasureSpec, childState) heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT)); }Copy the code

In the code above, measureChildWithMargins is measured on all child views, and if there is padding and margin, margins will also be calculated. After the above calculation, we get maxHeight and maxWidth values. Use setMeasuredDimension to save to the mMeasuredWidth and mMeasuredHeight members, which contain not only size but also state, so resolveSizeAndState is called to handle these two values first.

2. The layout process

After the performMeasure process, the size of each element in the ViewTree has been recorded, and the next step is to enter another traversal process, namely Layout, which is used by the ViewGroup to determine the location information of child elements.

public void layout(int l, int t, int r, int b) { ... // Record the four positions setFrame, L,t,r,b represents the distance from the upper left and lower right borders of the parent View. 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); . }... }Copy the code

The DecorView actually calls the View’s layout method. What it means is that you set the position of the View’s four vertices with the setFrame method. Once the View’s four vertices are determined, the position of the View in the parent container is determined. Let’s look at the FrameLayout onLayout method:

@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { layoutChildren(left, top, right, bottom, false /* no force left gravity */); } 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(); final int height = child.getMeasuredHeight(); //measure height 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; / / calculate childLeft 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; } // Calculate childTop, similar to calculating childLeft... } child.layout(childLeft, childTop, childLeft + width, childTop + height); }}}Copy the code

In the preceding code, we loop through all of the child views and then call the layout method and pass in the layout information for that View. In this case, we calculate the values childLeft and childTop. In the case of a rectangle, we get left, top, width and height and we know its size. After the layout is completed, the size and location of the view are determined.

3. The draw process

The draw can be executed only after the layout of an object is determined.

@CallSuper public void draw(Canvas canvas) { final int privateFlags = mPrivateFlags; mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN; // Step 1, draw the background, if needed int saveCount; drawBackground(canvas); final int viewFlags = mViewFlags; boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) ! = 0; boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) ! = 0; if (! verticalEdges && ! Edges) {// draw the content onDraw(canvas); // Step 3, draw the content onDraw(canvas); Step 4: Draw the children dispatchDraw(Canvas); drawAutofilledHighlight(canvas); // Overlay is part of the content and draws beneath Foreground if (mOverlay ! = null && ! mOverlay.isEmpty()) { mOverlay.getOverlayView().dispatchDraw(canvas); Come on down (foreground, scrollbars) onDrawForeground(Canvas); come on down (foreground, scrollbars) onDrawForeground(canvas); // Step 7, draw the default focus highlight drawDefaultFocusHighlight(canvas); if (isShowingLayoutBounds()) { debugDrawFocus(canvas); } // we're done... return; }... }Copy the code

Overall, the following steps are done:

  1. Draw the background
  2. Draw your own content
  3. Draw the childen
  4. Draw decoration (foreground/scroll bar)

conclusion

In this part, you should first sort out the whole View framework, such as ACtivity,Window, ViewRoot, WindowManagerImpl relationship, and then look at the entire ViewTree traversal and processing will be easier to understand.

The last

If this article provides valid information, please click a small like, if there is any discrepancy can comment or private message me, thanks.