Drawing process of Android View

Keywords: Android Windows xml Attribute

View rendering and event handling are two important topics. As I said before, the event distribution mechanism of View can be seen first by students who are not very clear about it. Delivery mechanism of Android Touch events There's no more to say here. For some functions that can not be achieved by the system's own controls, we need to draw by ourselves, if you need to master the drawing process of View skillfully.

1 Before formally talking about the rendering process of View, first understand the hierarchical relationship of Android's UI management system.

Next, I will introduce the meaning and function of each level.

1.1 PhoneWindow

PhoneWindow is the most basic window system in Android. Each activity creates a PhoneWindow object, which is the interface between Activity and the whole View system.

1.2 DecorView

DecorView is essentially a FrameLayout. DecorView is the ancestor of all Views in current Activity. It does not present anything to users. It has the following main functions, which may not be complete:

A. External events such as key, touch, trackball distributed by Dispatch ViewRoot;
B. DecorView has a direct sub-View, which we call System Layout, which is parsed from the Layout.xml of the system. It contains the style of the current UI, such as whether it has title, whether it has process bar, etc. These attributes can be called Window decorations.
C. As a bridge between Phone Windows and ViewRoot, ViewRoot sets window properties through DecorView.

1.3 System Layout

At present, android presupposes several UI styles according to user's needs. Through Phone Windows, we can get a layout with different Windows decorations by parsing the preset layout.xml. We call it System Layout. We add this System Layout to DecorView. At present, android provides eight kinds of System Layout, as shown below.

The default style can be set by the PhoneWindow method requestFeature(), which needs to be called before the setContentView() method is called.

1.4 Content Parent

Content Parent, the ViewGroup object, is the parent of the real ContentView. Our ContentView finally finds its host, which corresponds to a FrameLayout whose id is "content" in System Layout. This FrameLayout object includes the layout of our Activity (each System Layout has a FrameLayout with an id of "content".

1.5 Activity Layout

This activity Layout is the ContentView that we need to set to the window. Now we find that it is very low status, and this part is the UI part that interacts with user. The upper layers can not respond to and complete the desired purpose of user input.

2. The whole process of drawing

Now that we know that the whole View's Root is DecorView, where does the drawing of View begin? We know that each Activity creates a PhoneWindow object, which is the interface between Activity and the whole View system. Each Window corresponds to a View and a ViewRootImpl. Windows and View establish connections through ViewRootImpl. For Activity, ViewRootImp L is the link between Windows Manager and DecorView. The drawing entrance is initiated by the performance Traversals method of ViewRootImpl, such as Measure, Layout, Draw and so on.

Let's look at ViewRootImpl's performance Traversals method:

private void performTraversals() { 
...... 
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); 
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); 
...... 
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); 
......
mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
...... 
mView.draw(canvas); 
......
}

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

The mView we see in performance Traversals is actually DecorView. View drawing begins with DecorView. When mView.measure(), call getRootMeasureSpec to get two MeasureSpecs as parameters. The two parameters of getRootMeasureSpec (mWidth, lp.width) mWith and mHeight are the width and height of the screen, and LP is Windows Manager. LayoutParams, its lp.width and lp.height. The default value is MATCH_PARENT, so the measurement specification MeasureSpec generated by getRootMeasureSpec has a mode l of MATCH_PARENT and a size of the screen.

Drawing starts with the performTraversals() method of the root view ViewRoot, traverses the entire view tree from top to bottom, each View control draws itself, and the ViewGroup also needs to notify its own sub-View to draw. The process of view drawing can be divided into three operations: measurement, Layout and Draw.

3 Measure() process

To learn the Measure () process better, you have to know MeasureSpec. What the hell is this? This is the measurement mode, a static inner class in the View class, to illustrate how the View should be measured.

Everyone knows that a MeasureSpec is a combination of size and pattern. The value in MeasureSpec is an integer (32 bits) that packages size and mode into an Int type. The top two are mode, and the last 30 bits are size, in order to reduce the allocation cost of objects. MeasureSpec is similar to the figure below, except that the decimal number is used here, and MeasureSpec is stored in binary.

Note: -1 stands for EXACTLY and-2 for AT_MOST

3.1 MeasureSpec has three measurement modes:

  • UPSPECIFIED: Without specifying the measurement mode, the parent container has no restrictions on the child container. The size of the child container is as large as it wants. It is usually used in system development and rarely used in application development.
  • EXACTLY: Precise measurement mode. When layout_weight or layout_height of the view is specified as a specific value or match_parent, the parent container has set the size of the child container, and the child container should obey these boundaries, no matter how much space the child container wants.
  • AT_MOST: Maximum mode, when the layout_weight or layout_height of the view is specified as a specific value or wrap_content, the size of the child view can be any unit that does not exceed the maximum size allowed by the parent view.

For DecorView, its MeasureSpec is determined by the window size and its own Layout Params; for ordinary View, its MeasureSpec is determined by the Maysure Spec of the parent view and its own Layout Params.

3.2 Measure

Many articles will describe these three patterns as such, of course, including official documents, but this is not easy to understand. Especially if these three modes are combined with MATCH_PARENT and WRAP_CONTENT, many people will be confused. If the two MeasureSpecs of View. measure (int width MeasureSpec, int heightMeasureSpec) are passed from the parent class in terms of code, they are not entirely the requirements of the parent View, but are decided by the MasterSpec of the parent View and the Layout Params of the child View themselves, and the Layout Params of the child View is actually the layout_width and layout_height rotation we set when we write xml. It's an evolution. Let's first look at the code to make it clearer:

The measurement process of the parent View will measure the child View first, and then measure itself after the measurement results of the child View come out. The measurement Child With Margins above is used to measure a child View. Let's analyze how to measure it. See the commentary specifically:

protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { 

// Layout Params of Sub View, you are in layout_width and layout_height of xml.
// The value of layout_xxx is eventually encapsulated in this LayoutParams.
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();   

//According to the measurement specifications of the parent View and the parent View's own Padding,
//The Margin of the sub-View and the used space size (widthUsed) can be used to calculate the MasureSpec of the sub-View. The getChild MeasureSpec method can be used in the calculation process.
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);  

//By calculating the MassureSpec of the parent View and the Layout Params of the child View, the MassureSpec of the child View is calculated, and then the parent container is passed to the child container.
// Then let the sub-View measure itself with this MeasureSpec (a measurement requirement, such as not exceeding much), and if the sub-View is ViewGroup, it will be measured recursively down.
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

}

// The spec parameter represents the MesureSpec of the parent View 
// Padding parameter Parent View's Add + Child View's Margin, Parent View's size minus these margins, can be calculated accurately.
//               MesureSpec size of sub-View
// The child Dimension parameter represents the value (lp.width or lp.height) of the LayoutParams attribute inside the child View.
//                    It can be wrap_content, match_parent, an exact size.  
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {  
    int specMode = MeasureSpec.getMode(spec);  //Get the mode of the parent View  
    int specSize = MeasureSpec.getSize(spec);  //Get the size of the parent View  

   //The size of the parent View - your own Padding + Margin of the child View, and the value is the size of the child View.
    int size = Math.max(0, specSize - padding);   

    int resultSize = 0;    //Initialize the value, and finally generate MesureSpec for the child View through these two values
    int resultMode = 0;    //Initialize the value, and finally generate MesureSpec for the child View through these two values

    switch (specMode) {  
    // Parent has imposed an exact size on us  
    //1. Father View is EXACTLY!  
    case MeasureSpec.EXACTLY:   
        //1.1. The width or height of the child View is an exact size.  
        if (childDimension >= 0) {            
            resultSize = childDimension;         //size is the exact value  
            resultMode = MeasureSpec.EXACTLY;    //Model is EXACTLY.  
        }   
        //1.2. The width or height of the child View is MATCH_PARENT/FILL_PARENT   
        else if (childDimension == LayoutParams.MATCH_PARENT) {  
            // Child wants to be our size. So be it.  
            resultSize = size;                   //Size is the parent view size  
            resultMode = MeasureSpec.EXACTLY;    //Model is EXACTLY.  
        }   
        //1.3. The width or height of the child View is WRAP_CONTENT  
        else if (childDimension == LayoutParams.WRAP_CONTENT) {  
            // Child wants to determine its own size. It can't be  
            // bigger than us.  
            resultSize = size;                   //Size is the parent view size  
            resultMode = MeasureSpec.AT_MOST;    //Model is AT_MOST.  
        }  
        break;  

    // Parent has imposed a maximum size on us  
    //2. The parent View is AT_MOST!      
    case MeasureSpec.AT_MOST:  
        //2.1. The width or height of the child View is an exact size.  
        if (childDimension >= 0) {  
            // Child wants a specific size... so be it  
            resultSize = childDimension;        //size is the exact value  
            resultMode = MeasureSpec.EXACTLY;   //Model is EXACTLY.  
        }  
        //2.2. The width or height of the child View is MATCH_PARENT/FILL_PARENT  
        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;                  //Size is the parent view size  
            resultMode = MeasureSpec.AT_MOST;   //Model is AT_MOST  
        }  
        //2.3. The width or height of the child View is WRAP_CONTENT  
        else if (childDimension == LayoutParams.WRAP_CONTENT) {  
            // Child wants to determine its own size. It can't be  
            // bigger than us.  
            resultSize = size;                  //Size is the parent view size  
            resultMode = MeasureSpec.AT_MOST;   //Model is AT_MOST  
        }  
        break;  

    // Parent asked to see how big we want to be  
    //3. The parent View is UNSPECIFIED!  
    case MeasureSpec.UNSPECIFIED:  
        //3.1. The width or height of the child View is an exact size.  
        if (childDimension >= 0) {  
            // Child wants a specific size... let him have it  
            resultSize = childDimension;        //size is the exact value  
            resultMode = MeasureSpec.EXACTLY;   //Model is EXACTLY  
        }  
        //3.2. The width or height of the child View is MATCH_PARENT/FILL_PARENT  
        else if (childDimension == LayoutParams.MATCH_PARENT) {  
            // Child wants to be our size... find out how big it should  
            // be  
            resultSize = 0;                        //size is 0! The value is undetermined.  
            resultMode = MeasureSpec.UNSPECIFIED;  //Model is UNSPECIFIED  
        }   
        //3.3. The width or height of the child View is WRAP_CONTENT  
        else if (childDimension == LayoutParams.WRAP_CONTENT) {  
            // Child wants to determine its own size.... find out how  
            // big it should be  
            resultSize = 0;                        //size is 0!, its value is undetermined  
            resultMode = MeasureSpec.UNSPECIFIED;  //Model is UNSPECIFIED  
        }  
        break;  
    }  
    //MeasureSpec objects are constructed based on the mode l and size obtained from the above logical conditions.  
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);  
}

The code above is a little bit too much. I hope you can read some comments carefully. The code is written a lot. In fact, the calculation principle is very simple.
1. If we write all the values in layout_width or layout_height of xml, then the above measurement is completely unnecessary. The reason for the above measurement is that match_parent is full of parent container, wrap_content is as big as it is. When we write code, it's very cool. When we code conveniently, google will help us calculate your match. _ How big is the parent and how big is the wrap_content? This calculation process is that the Maysure Spec of the parent View is continuously passed to the child View, and then the Maysure Spec of the child View is calculated with the Layout Params of the child View. Then the child View is continuously passed to the child View, calculating the Maysure Spec of each View. Only when the child View has MeasureSpec, can the child View measure itself and its own child View more.

The above code is easy to understand in this way.

  • If the MesureSpec of the parent View is EXACTLY, it means that the size of the parent View is exact. (The exact meaning is well understood. If a MesureSpec of the view is EXACTLY, how big is its size, and it must be that big when it is finally displayed on the screen.)

    If the layout_xxxx of the child View is MATCH_PARENT, the size of the parent View is exact, and the size of the child View is full of the parent View, then the size of the child View must be exact, and the size value is the size of the parent View. So the size of the child View = the size of the parent View, mode=EXACTLY

    b. If the layout_xxxx of the child view is WRAP_CONTENT, that is, the size of the child view is determined by its own content, but after all, the child view is a child view, the size can not exceed the size of the parent view, but the child view is WRAP_CONTENT, we do not know the size of the specific child view, until the child. measure (child Width Measure Spec, Heightmeasure Spec) call. When you really measure the content of the child view (such as TextView wrap_content), you need to measure the size of the text view content, that is, the size of the character occupancy. This measurement is when the child. measure (child Width Measure Spec, child HeightMeasure Spec) can measure the size of the character. Measure Spec means assuming that your character is 100px, but MeasureSpec requires only 50px, which is cut off at this time. Through the above description, the child View MeasureSpec mode l should be AT_MOST, while the size of the temporary parent View of size, meaning that the size of the child View has no exact value, the maximum size of the child View is the size of the parent View, can not exceed the size of the parent View (that is, the meaning of AT_MOST), and then the measure as a parameter of the child View measure method, as the child View measure. Size constraints or requirements, with this MeasureSpec sub-View to implement their own measurements.

    c. If the layout_xxxx of the child View is a definite value (200dp), then it's simpler. No matter what the mode and size of your parent View are, I've written 200 dp. Then the final display of the control is 200dp. No matter how big my parent View is or how big my own content is, anyway, I'm that big. So the mode of MeasureSpec = EXACT = EXACT LY size = the value you filled in in layout_xxx.

  • If the MesureSpec of the parent View is AT_MOST, the size of the parent View is uncertain, and the maximum size is the size value of MeasureSpec, which cannot exceed this value.

    a. If the layout_xxxx of the child View is MATCH_PARENT, the size of the parent View is uncertain (only know the maximum size), and the size of the child View is MATCH_PARENT (full of the whole parent View), then the size of the child View is uncertain even if you are full of the parent container, and the parent View can't determine its own size, so you can't determine the size of the MATCH_PARENT. The mode=AT_MOST, size = the size of the parent View, that is, you write in the layout of MATCH_PARENT, but because your father's container size is uncertain, resulting in the size of the child View is uncertain, only know that the largest is the size of the parent View.

    b. If the layout_xxxx of the child View is WRAP_CONTENT, the size of the parent View is uncertain (only know how big the maximum is), and the child View is WRAP_CONTENT, then the size of the child View is the size of the parent View before the child View's Content calculates the size, so the child View MeasureSpec is AT_MOST, and size of the temporary parent View is size.

    c. If the layout_xxxx of the child View is a definite value (200dp), as above, how much is written will not change.

  • If the MesureSpec of the parent View is UNSPECIFIED (not specified), it means that there are no constraints or constraints, unlike AT_MOST, it means that the maximum size can only be as large as that of the parent View, or EXACTLY, it means that the child View can get whatever size it wants and is not constrained.

    If the layout_xxxx of the child View is MATCH_PARENT, because the MesureSpec of the parent View is UNSPECIFIED, the size of the parent View itself does not have any constraints and requirements,
    So for sub-View, no matter WRAP_CONTENT or MATCH_PARENT, sub-View is not bound by any restrictions. As much as you want, there are no more requirements than many. Once there are no requirements and constraints, the value of size is meaningless, so it is usually set directly to 0.

    b. Ibid.

    c. If the layout_xxx of the child View is a definite value (200dp), as above, how much is written will not change (remember, only the exact value is set, then no matter how measured, the size is the same, it is the value you wrote).

So far, we've finished talking about MeasureSpec and the three modes, WRAP_CONTENT and MATCH_PARENT, and fixed values. Here's the onMeasure() method.

3.3 The measurement process of View is mainly based on onMeasure() method.

Open the source code of View and find the measurement method. This method has a lot of code, but the measurement work is done in onMeasure(). The measurement method is final, so this method can not be rewritten. If you want to customize the measurement of View, you should rewrite the onMeasure() method.

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
  ......
  onMeasure(widthMeasureSpec,heightMeasureSpec);
  .....
}

Continue to track the onMeasure () method:

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

The onMeasure method of View is implemented by default simply by calling setMeasuredDimension(). setMeasuredDimension() can be simply understood as setting values for mMeasuredWidth and mMeasuredHeight. If these two values are set, it means that the measurement of the View is over and the width of the View has been measured. If we want to set the width of a View, we can set the width of a View directly by setMeasuredDimension (100, 200), but the setMeasuredDimension method must be called in the onMeasure method, otherwise an exception will be thrown. Let's see how the default width and height are obtained for View.

//Get the value of the android:minHeight attribute or the size of the View background image
protected int getSuggestedMinimumWidth() { 
   return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth()); 
} 
//@ The param size parameter generally indicates that the android:minHeight attribute is set or the size of the View background image is set.  
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:        //The parent view representing the size of the View is undetermined and set to the default value 
     result = size;  
     break;    
   case MeasureSpec.AT_MOST:    
   case MeasureSpec.EXACTLY:        
     result = specSize;  
     break;   
 }    
return result;
}

The first parameter size of getDefaultSize is equal to the value returned by getSuggested Minimum XXXX (the recommended minimum width and height), and the recommended minimum width and height are determined by both the Background size of View and the minXXX attribute of View, which can be understood as the default length of View, while the second parameter, MeasureSpec, is passed from the parent View to its own MeasureSp. Ec, this MeasureSpec is calculated by measurement. Before explaining the specific calculation and measurement process, it is quite clear that MeasureSpec (determined by the MassureSpec of the parent View and Layout Params of the child View) will be used as the height of the View by default as long as the mode l of the test is not UNSPECIFIED (undetermined).

The default measurement for View is simple. In most cases, the size of MeasureSpec calculated is taken as the final measurement size. For other derivative classes of View, such as TextView, Button, ImageView, etc., their onMeasure method system has been rewritten. It is not so simple to take the size of MeasureSpec directly as the size, but to measure the height of characters or pictures first, and then to get the height of View itself content (character height, etc.), if MeasureSpec is AT_MOST, and View itself content is not higher than the size of MeasureSpec, so you can directly use the height of View itself content (character height, etc.) instead of directly using the size of MeasureSpec as the size of View as View.java.

3.4 ViewGroup's Makesure Process

The ViewGroup class does not implement onMeasure. We know that the measurement process is actually done in the onMeasure method. Let's look at the onMeasure method of FrameLayout and analyze it in detail.

//Measurement of FrameLayout
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
....
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) {   
    // Traversing through your own sub-View s, measureChildWithMargins is the top method as long as it's not GONE.
    // The source code has already been mentioned. If you forget to look back, the basic idea is that Father View has its own MesureSpec. 
    // Pass it to the child View and combine the Layout Params of the child View to calculate the MesureSpec of the child View, and then continue to upload.
    // Pass the leaf node, the leaf node has no child View, just measure yourself according to the MeasureSpec passed down.
     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);  
     ....
     ....
   }
}
.....
.....
//After measuring all the children, after calculating a series of classes, they set their width and height by setting Measured Dimension.
//For FrameLayout, the maximum possible size of the word View, and for LinearLayout, it may be the accumulation of height.
//The principle of specific measurement to see the source code. In general, the parent View measures itself after all the child View measurements are completed.
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),        
resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT));
....
}

At this point, we have basically gone through the main principles of Measure. If you don't know something or make a mistake, you can leave a message below.

4. layout process

private void performTraversals() { 
......
mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
...... 

After performing mView.measure calculates mMeasuredXXX, the performance Traversals method starts to execute the layout function to determine where the View is located. At present, the calculated View only knows the size of the view matrix and where the matrix is located. This is the work of layout. The main function of layout is to place the view tree in the right place according to the size of the sub-view and the layout parameters.

Now that it's through mView. layout (0, 0, mView. getMeasuredWidth (), mView. getMeasuredHeight (); let's see what the layout function does. mView must be a ViewGroup, not a View. Let's look directly at the layout function of the ViewGroup.

public final void layout(int l, int t, int r, int b) {    
   if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {        
       if (mTransition != null) {            
           mTransition.layoutChange(this);        
        }       
        super.layout(l, t, r, b);    
    } else {        
    // record the fact that we noop'd it; request layout when transition finishes        
      mLayoutCalledWhileSuppressed = true;    
   }
}

As the code can see, LayoutTransition is used to deal with the animation effect of adding and deleting subviews in ViewGroup. That is to say, if the current ViewGroup does not add LayoutTransition animation, or if the LayoutTransition animation is not running at the moment, then call super.layout(l, t, r, b), and then call onLayout in ViewGroup, otherwise set mLayoutSuppress to true. Call requestLayout() when the animation is completed.
This function is final and cannot be overridden, so all the subclasses of ViewGroup call this function. The implementation of layout is done in super.layout(l, t, r, b). So let me take a look at the layout function of View class.

public final void layout(int l, int t, int r, int b) {
       .....
      //Set the coordinate axis of the View in the parent view
       boolean changed = setFrame(l, t, r, b); 
       //Determine if the position of the View has changed and see if it is necessary to re-lay it out
       if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
           if (ViewDebug.TRACE_HIERARCHY) {
               ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
           }
           //Call onLayout(changed, l, t, r, b); function
           onLayout(changed, l, t, r, b);
           mPrivateFlags &= ~LAYOUT_REQUIRED;
       }
       mPrivateFlags &= ~FORCE_LAYOUT;
       .....
   }

1. setFrame(l, t, r, b) can be understood as assigning values to mLeft, mTop, mRight, mBottom, and then it can basically determine the position of the View itself in the parent view. The rectangular area composed of these values is the position of the View, where the specific position is relative to the parent view.

2. Callback onLayout. For View, onLayout is just an empty implementation. Normally, we don't need to overload the function.

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {  

}

For ViewGroup, the only difference is that there is an additional modification of the keyword abstract in ViewGroup, requiring its subclasses to overload onLayout functions.

@Override  
protected abstract void onLayout(boolean changed,  
        int l, int t, int r, int b);

The purpose of overloading onLayout is to arrange the specific location of its children in the parent View. So how to arrange the specific location of its children View?

 int childCount = getChildCount() ; 
  for(int i=0 ;i<childCount ;i++){
       View child = getChildAt(i) ;
       //The entire layout() process is a recursive process
       child.layout(l, t, r, b) ;
    }

The code is very simple. It traverses your child and then calls child.layout(l, t, r, b) to locate the child view through the setFrame(l, t, r, b). The key point is (l, t, r, b) how to calculate it. Remember the measuring Width and Mesured Height that we measured before? Remember the Gravity you set up in xml? Are there any other parameters of Relative Layout that, yes, work with Measured Height and Measured Width to determine the exact location of the child View in the parent view? Specific calculation process you can see the simplest framework Layout onLayout function source code, each different view group implementation is different, here do not do a specific analysis.

3. Measured Width and Mesured Height provide an important basis for the layout process (if you don't know the size of View, how can you fix the position of four points). But these two parameters are not necessary. The four parameters l, t, r, b in the layout process can be arbitrarily specified by us, and the final layout position and size of View (m - mLeft)= The actual width or mBottom-mTop = actual height) is entirely determined by these four parameters. The mMeasuredWidth and m MeasuredHeight obtained by the measurement process provide the values of view size measurements, but we can completely avoid these two values, so the measurement process is not necessary. If we don't use these two values, getMeasuredWidth() and getWidth() are likely to be different values, and their calculations are different:

public final int getMeasuredWidth() {  
        return mMeasuredWidth & MEASURED_SIZE_MASK;  
    }  
public final int getWidth() {  
        return mRight - mLeft;  
    }

So far, the Layout process has been finished. It's relatively simple. If you don't know something about it or make mistakes, you can leave a message below.

5.draw process

The next step of the performance Traversals method is mView.draw(canvas); because the View drawing method is not usually rewritten, and the official documents also suggest not to rewrite the draw method, so the next step is to execute the View.java drawing method. Let's look at the source code:

public void draw(Canvas canvas) {
    ...
        /*
         * 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
        // Step 1: Draw the background of the View
    ...
        background.draw(canvas);
    ...
        // skip step 2 & 5 if possible (common case)
    ...
        // Step 2, save the canvas' layers
        // Step 2: If necessary, save the canvas layer to prepare for fading
    ...

        if (solidColor == 0) {
            final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;

            if (drawTop) {
                canvas.saveLayer(left, top, right, top + length, null, flags);
            }
    ...
        // Step 3, draw the content
        // Step 3: Draw the contents of the View
        if (!dirtyOpaque) onDraw(canvas);

        // Step 4, draw the children
        // Step 4: Draw a child view of View
        dispatchDraw(canvas);

        // Step 5, draw the fade effect and restore layers
        // Step 5: Draw fading edges of View and restore layers if necessary

        if (drawTop) {
            matrix.setScale(1, fadeHeight * topFadeStrength);
            matrix.postTranslate(left, top);
            fade.setLocalMatrix(matrix);
            canvas.drawRect(left, top, right, top + length, p);
        }
    ...
        // Step 6, draw decorations (scrollbars)
        // Step 1: Draw the decoration of the View (for example, scrollbars)

        onDrawScrollBars(canvas);
    }

Write clearly. Let's come one by one.
1. Step 1: Background rendering
Just look at the notes, not the main point.

private void drawBackground(Canvas canvas) { 
     Drawable final Drawable background = mBackground; 
      ...... 
     //mRight - mLeft, mBottom - mTop layout determines four points to set the drawing area of the background 
     if (mBackgroundSizeChanged) { 
        background.setBounds(0, 0, mRight - mLeft, mBottom - mTop);   
        mBackgroundSizeChanged = false; rebuildOutline(); 
     } 
     ...... 
     //Call Drawable's draw() to draw the background picture onto the canvas
     background.draw(canvas); 
     ...... 
}

2. The third step is to draw the content of View.
onDraw(canvas) method is view used to draw its own, how to draw, what style of color lines need to be implemented by sub-View itself, onDraw(canvas) of View.java is empty, ViewGroup is not implemented, the content of each View is different, so it is necessary to implement specific logic by sub-classes.

Step 4: Draw all the subviews of the current View
The dispatchDraw(canvas) method is used to draw sub-views. The dispatchDraw() method of View.java is an empty method. Because View has no sub-views and does not need to implement dispatchDraw() method, ViewGroup is different. It implements dispatchDraw() method:

@Override
 protected void dispatchDraw(Canvas canvas) {
       ...
        if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
            for (int i = 0; i < count; i++) {
                final View child = children[i];
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                    more |= drawChild(canvas, child, drawingTime);
                }
            }
        } else {
            for (int i = 0; i < count; i++) {
                final View child = children[getChildDrawingOrder(count, i)];
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                    more |= drawChild(canvas, child, drawingTime);
                }
            }
        }
      ......
    }

At first glance, the code shows that traversing the sub-View and drawing Child (), the drawChild() method actually calls the sub-View. drawing () method. The ViewGroup class has already implemented the default process of drawing the sub-View for us. This implementation basically meets most of the requirements, so the sub-class of ViewGroup (Linear Layout, FrameLayout) basically does not override the dispatchDraw method. We are implementing customization. The core process of drawChild() is to allocate the appropriate cavas clipping area for the sub-view. The size of the clipping area is determined by the layout process, and the position of the clipping area depends on the scroll value and the current animation of the sub-view. After setting the clipping area, the draw() function of the subview will be called to draw the concrete drawing.

4. Step 6 Drawing the Scrollbar of View
It's not the point. Just know about it. An annotation from onDrawScrollBars: Request the drawing of the horizontal and the vertical scrollbar. The scrollbars are painted only if they have been awakened first.

Look at the recursive flow of draw s in one graph.

The drawing process of the real View has been finished. It's a bit long. Please calm down and read it carefully. If you don't know anything about it, you can leave a message below.

Reference resources:
Windows Mechanism Analysis of android-UI Management System
Drawing process of View

Posted by artisticre on Tue, 18 Jun 2019 16:32:38 -0700