[Android] Proceso de dibujo de Android View

La actividad en Android existe como el operador de la aplicación, lo que representa una interfaz de usuario completa. Proporciona una ventana para dibujar varias vistas. Cuando la actividad comienza, estableceremos una vista de contenido a través del método setContentView. Esta vista de contenido es el usuario Vea la interfaz.

Jerarquía del sistema de gestión de la interfaz de usuario

PhoneWindow es el sistema de ventanas más básico del sistema Android y se crea uno para cada actividad. PhoneWindow es la interfaz para la interacción entre el sistema Activity y View. DecorView es esencialmente un FrameLayout y es el antepasado de todas las Vistas en Actividad.

El proceso general de dibujo

Cuando se inicia una aplicación, iniciará una actividad principal y el sistema Android la dibujará de acuerdo con el diseño de la actividad. El dibujo comenzará desde el método performTraversals () de la vista raíz ViewRoot y recorrerá todo el árbol de la vista de arriba a abajo. Cada control de vista es responsable de dibujar a sí mismo, y ViewGroup también debe ser responsable de notificar a sus vistas secundarias para realizar operaciones de dibujo. El proceso de operación de la vista se puede dividir en tres pasos, a saber, medir, diseñar y dibujar. El método performTraversals está en la clase ViewRootImpl y su código principal es el siguiente:

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

MeasureSpec

MeasureSpec representa un valor entero de 32 bits. Los 2 bits superiores representan el modo de medición SpecMode y los 30 bits inferiores representan el SpecSize en un determinado modo de medición. MeasureSpec es una clase interna estática de la clase Vista, que se utiliza para explicar cómo medir esta Vista.
Tres modos de medición.

SIN ESPECIFICAR: El modo de medición no está especificado. La vista principal no limita el tamaño de la vista secundaria. La vista secundaria puede ser del tamaño que desee. Por lo general, se usa dentro del sistema y rara vez se usa en el desarrollo de aplicaciones.
EXACTAMENTE: Modo de medición exacta. Tiene efecto cuando el ancho de diseño o la altura de diseño de la vista se especifica como un valor específico o
match_parent, lo que indica que la vista principal ha determinado el tamaño exacto de la vista secundaria. En este modo, el valor de medición de la Vista es el valor de SpecSize.
AT_MOST: modo máximo. Tiene
efecto cuando el ancho de diseño o altura de diseño de la vista actual se especifica como wrap_content . En este momento, el tamaño de la vista secundaria puede ser cualquier tamaño que no exceda el tamaño máximo de la vista principal. Para DecorView, su MeasureSpec está
determinado por el tamaño de la ventana y sus propios LayoutParams; para la vista normal, su MeasureSpec está determinado por el
MeasureSpec de la vista principal y sus propios LayoutParams.

Medida

Medir se utiliza para calcular el tamaño real de la Vista. El proceso de medición de la página comienza con el método performMeasure.

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

La operación específica se distribuye al ViewGroup y el ViewGroup la pasa a la View secundaria en su método measureChild. ViewGroup implementa operaciones de medición atravesando todas sus vistas secundarias y llamando al método de medición de las vistas secundarias una por una.

  // 遍历测量 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);
  }

En el método Measure de View (ViewGroup), la medición final se logra a través del método callback onMeasure, que generalmente es implementado por la subclase específica de View. Puede implementar la Vista personalizada anulando este método.

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

Diseño

El proceso de Diseño se utiliza para determinar la posición de diseño de la Vista en el contenedor principal. Después de que el contenedor principal obtiene el parámetro de posición de la Vista secundaria, llama al método de diseño de la Vista secundaria y pasa el parámetro de posición a la implementación. El código performLayout de ViewRootImpl es el siguiente:

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

Ver el código del método de diseño:

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

Dibujar

La operación Dibujar se usa para dibujar el control El proceso de dibujo comienza con el método performDraw. El método performDraw está en la clase ViewRootImpl y su código principal es el siguiente:

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

Finalmente, se llama al método de dibujo de cada Vista para dibujar cada Vista específica. El dibujo se puede dividir básicamente en seis pasos:

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

Supongo que te gusta

Origin blog.csdn.net/sinat_36955332/article/details/108614620
Recomendado
Clasificación