android view的绘制流程

当一个应用启动的时候,会启动一个主activity,android系统会根据activity的布局来对它进行绘制。每个view负责绘制自己,而viewgroup还需要负责通知自己的子view进行绘制操作。视图绘制的过程可以分为三个步骤,分别是 Measure LayoutDraw

private void performTraversals() {
        ......
        //最外层的根视图的widthMeasureSpec和heightMeasureSpec由来
        //lp.width和lp.height在创建ViewGroup实例时等于MATCH_PARENT
        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);
        ......
    }

Measure

Measure操作用来计算View的实际大小,对于viewGroup来说,由viewGroup在它的measureChild方法中传递给子view,viewGroup通过遍历自身所有的子view,并逐个调用子view的measure方法完成测量工作。当测量某个指定的view的时候,根据父容器的MeasureSpec和子view的LayoutParams等信息来计算子view的MeasureSpec

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) {   
    // 遍历自己的子View,只要不是GONE的都会参与测量,
     measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);       
     ...
     ....
   }
}
//测量某个指定的view
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { 

// 子View的LayoutParams,你在xml的layout_width和layout_height,
// layout_xxx的值最后都会封装到这个个LayoutParams。
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();   

//根据父容器的MeasureSpec和子view的LayoutParams等信息来计算子view的MeasureSpec

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

//通过父View的MeasureSpec和子View的自己LayoutParams的计算,算出子View的MeasureSpec,然后父容器传递给子容器的
// 然后让子View用这个MeasureSpec(一个测量要求,比如不能超过多大)去测量自己,如果子View是ViewGroup 那还会递归往下测量。
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

}

对于每个View的measure方法,最终的测量是通过回调onMeasure方法实现的。这个通常由view的特定子类自己实现,卡发着也可以通过重写这个方法实现自定义view。

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来获得view的宽高
protected int getSuggestedMinimumWidth() { 
   return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth()); 
} 
//@param size参数一般表示设置了android:minHeight属性或者该View背景图片的大小值  
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:        //表示该View的大小父视图未定,设置为默认值 
     result = size;  
     break;    
   case MeasureSpec.AT_MOST:    
   case MeasureSpec.EXACTLY:        
     result = specSize;  
     break;   
 }    
return result;
}

主要要三种测量模式,

  • UNSPECIFIED :不指定测量模式,俯视图没有限制子视图饿大小,子视图可以使想要的任何尺寸,通常用于系统内部,应用开发很少用到

  • EXACTLY 精确测量模式,当该视图的layout_width 或者layout_height 指定为具体数值或者match_parent时生效,表示父视图已经决定了子视图的精确大小,这种模式下View的测量值就是SpecSize的值

  • AT_MOST 最大值模式,当该视图的layout_width或者layout_height指定为wrap_content时生效,此时,子视图的尺寸可以使不超过父视图允许的最大尺寸的任何尺寸。

Layout

Layout 过程用来确定View在父容器中的布局位置,它是由父容器获取子View的位置参数后,调用子View的layout方法并将位置参数传入实现的,

Draw

Draw用来将控件绘制出来

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, 如果需要的话,绘制view背景
    ...
        background.draw(canvas);
    ...
        // skip step 2 & 5 if possible (common case) 一般情况会跳过第二步和第五步
    ...
        // Step 2, save the canvas layers  如果需要的话,保存canvas图层,为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 绘制view的内容
        if (!dirtyOpaque) onDraw(canvas);

        // Step 4, draw the children 绘制view的子view
        dispatchDraw(canvas);

        // Step 5, draw the fade effect and restore layers 如果需要的话,绘制View的fading边缘来恢复图层

        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) 绘制View的装饰例如滚动条()
        onDrawScrollBars(canvas);
    }

猜你喜欢

转载自blog.csdn.net/u011337574/article/details/79899088