[Android] Android View drawing process

Activity in Android exists as the carrier of the application, which represents a complete user interface. It provides a window to draw various views. When the Activity starts, we will set a content view through the setContentView method. This content view is the user See the interface.

UI management system hierarchy

PhoneWindow is the most basic window system in the Android system, and one is created for each Activity. PhoneWindow is the interface for interaction between Activity and View system. DecorView is essentially a FrameLayout and is the ancestor of all Views in Activity.

The overall process of drawing

When an application starts, it will start a main activity, and the Android system will draw it according to the layout of the activity. The drawing will start from the performTraversals() method of the root view ViewRoot, and traverse the entire view tree from top to bottom. Each View control is responsible for drawing itself, and the ViewGroup also needs to be responsible for notifying its child views to perform drawing operations. The process of view operation can be divided into three steps, namely Measure, Layout and Draw. The performTraversals method is in the ViewRootImpl class, and its core code is as follows:

  int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
  int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
  ...
  // 测量
  performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
  ...
  // 布局
  performLayout(lp, mWidth, mHeight);
  ...
  // 绘制
  performDraw();

MeasureSpec

MeasureSpec represents a 32-bit integer value. The upper 2 bits represent the measurement mode SpecMode, and the lower 30 bits represent the SpecSize in a certain measurement mode. MeasureSpec is a static internal class of the View class, used to explain how to measure this View.
Three measurement modes.

UNSPECIFIED: The measurement mode is not specified. The parent view does not limit the size of the child view. The child view can be any size you want. It is usually used inside the system and is rarely used in application development.
EXACTLY: Exact measurement mode. It takes effect when the layout_width or layout_height of the view is specified as a specific value or
match_parent, indicating that the parent view has determined the exact size of the child view. In this mode, the measurement value of the View is the value of SpecSize.
AT_MOST: Maximum mode. It
takes effect when the layout_width or layout_height of the current view is specified as wrap_content . At this time, the size of the child view can be any size that does not exceed the maximum size of the parent view. For DecorView, its MeasureSpec is
determined by the window size and its own LayoutParams; for ordinary View, its MeasureSpec is determined by the parent view's
MeasureSpec and its own LayoutParams.

Measure

Measure is used to calculate the actual size of the View. The measurement process of the page starts with 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);
    }
  }

The specific operation is distributed to the ViewGroup, and passed to the child View by the ViewGroup in its measureChild method. ViewGroup implements measurement operations by traversing all its child views and calling the measure method of the child views one by one.

  // 遍历测量 ViewGroup 中所有的 View
  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);
        }
    }
  }
 
  // 测量某个指定的 View
  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);
  }

In the Measure method of View (ViewGroup), the final measurement is achieved through the callback onMeasure method, which is usually implemented by the specific subclass of View itself. You can implement custom View by overriding this method.

  public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    
    
    ...
    onMeasure(widthMeasureSpec, heightMeasureSpec);
    ....
  }
  
  // 如果需要自定义测量,子类需重写这个方法
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
    
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
    getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
  }
  
  // 如果 View 没有重写onMeasure 方法,默认会直接调用 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;
   }

Layout

The Layout process is used to determine the layout position of the View in the parent container. After the parent container obtains the position parameter of the child View, it calls the layout method of the child View and passes the position parameter to the implementation. The performLayout code of ViewRootImpl is as follows:

  private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
    
    
    ...
    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    ...
  }

View's layout method code:

  public void layout(int l, int t, int r, int b) {
    
    
    onLayout(changed, l, t, r, b);
  }
 
  // 空方法,子类如果是 ViewGroup 类型,则重写这个方法,实现 ViewGroup 中所有 View 控件布局
  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    
    
  }

Draw

The Draw operation is used to draw the control. The drawing process starts with the performDraw method. The performDraw method is in the ViewRootImpl class, and its core code is as follows:

  private void performDraw() {
    
    
    boolean canUseAsync = draw(fullRedrawNeeded);
  }
 
  private boolean draw(boolean fullRedrawNeeded) {
    
    
    ...
    if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
                        scalingRequired, dirty, surfaceInsets)) {
    
    
      return false;
    }
  }
 
  private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
    
    
     ...
     mView.draw(canvas);
     ...
  }

Finally, the draw method of each View is called to draw each specific View. The drawing can basically be divided into six steps:

  public void draw(Canvas canvas) {
    
    
    ...
    // Step 1, draw the background, if needed
    if (!dirtyOpaque) {
    
    
      drawBackground(canvas);
    }
    ...
    // Step 2, save the canvas' layers
    saveCount = canvas.getSaveCount();
    ...
    // Step 3, draw the content
    if (!dirtyOpaque) onDraw(canvas);
 
    // Step 4, draw the children
    dispatchDraw(canvas);
 
    // Step 5, draw the fade effect and restore layers
    canvas.drawRect(left, top, right, top + length, p);
    ...
    canvas.restoreToCount(saveCount);
    ...
    // Step 6, draw decorations (foreground, scrollbars)
    onDrawForeground(canvas);
  }

Guess you like

Origin blog.csdn.net/sinat_36955332/article/details/108614620