Android View 的绘制流程

测量

经过 measure() -> onMeasure() -> setMeasuredDimension() 等函数的调用,最终 View 自身测量流程执行完毕。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
    // 1.通过遍历,对每个child进行测量
    for(int i = 0 ; i < getChildCount() ; i++){  
        View child = getChildAt(i);

        // 2.计算新的布局要求,并对子控件进行测量
        measureChild(child, widthMeasureSpec, heightMeasureSpec);
    }
    
    ...
}

protected void measureChild(View child, int parentWidthMeasureSpec,
            int parentHeightMeasureSpec) {
    final LayoutParams lp = child.getLayoutParams();

    // 通过 padding 值,计算出子控件的布局要求
    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
            mPaddingLeft + mPaddingRight, lp.width);
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
            mPaddingTop + mPaddingBottom, lp.height);

    // 将新的布局要求传入 measure 方法,完成子控件的测量
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

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

    // 开发者需要自己重写onMeasure函数,以自定义测量逻辑
    onMeasure(widthMeasureSpec, heightMeasureSpec);
}

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


protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
    ...

    // 该方法的本质就是将测量结果存起来,以便后续的layout和draw流程中获取控件的宽高
    mMeasuredWidth = measuredWidth;
    mMeasuredHeight = measuredHeight;
}

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;
      // match_parent、wrap_content则返回布局要求中的size值
      case MeasureSpec.AT_MOST:
      case MeasureSpec.EXACTLY:
          result = specSize;
          break;
    }
    return result;
}

// 在某些情况下(比如自身设置了minWidth或者background属性),View需要通过getSuggestedMinimumWidth()函数作为默认的宽度值:
protected int getSuggestedMinimumWidth() {
    return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    // 获取父 View 的测量模式
    int specMode = MeasureSpec.getMode(spec);

    // 获取父 View 的测量大小
    int specSize = MeasureSpec.getSize(spec);

    // 父 View 计算出的子 View 的大小
    int size = Math.max(0, specSize - padding);

    int resultSize = 0;
    int resultMode = 0;
    switch (specMode) {
    case MeasureSpec.EXACTLY:
        // 子View的高度或宽度>0说明其实一个确切的值,因为 match_parent 和 wrap_content 的值是<0的
        if (childDimension >= 0) {
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
            //子View的高度或宽度为match_parent
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            resultSize = size;//将size即父View的大小减去边距值所得到的值赋值给resultSize
            resultMode = MeasureSpec.EXACTLY;//指定子View的测量模式为EXACTLY
           //子View的高度或宽度为wrap_content
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            resultSize = size;//将size赋值给result
            resultMode = MeasureSpec.AT_MOST;//指定子View的测量模式为AT_MOST
        }
        break;
    //如果父容器的测量模式是AT_MOST
    case MeasureSpec.AT_MOST:
        if (childDimension >= 0) {
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            resultSize = size;
            // 因为父View的大小是受到限制值的限制,所以子View的大小也应该受到父容器的限制并且不能超过父View  
            resultMode = MeasureSpec.AT_MOST;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;
    //如果父容器的测量模式是UNSPECIFIED即父容器的大小未受限制
    case MeasureSpec.UNSPECIFIED:
        //如果自View的宽和高是一个精确的值
        if (childDimension >= 0) {
              //子View的大小为精确值
            resultSize = childDimension;
            //测量的模式为EXACTLY
            resultMode = MeasureSpec.EXACTLY;
            //子View的宽或高为match_parent
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
              //因为父View的大小是未定的,所以子View的大小也是未定的
            resultSize = 0;
            resultMode = MeasureSpec.UNSPECIFIED;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            resultSize = 0;
            resultMode = MeasureSpec.UNSPECIFIED;
        }
        break;
    }
    //根据resultSize和resultMode调用makeMeasureSpec方法得到测量要求,并将其作为返回值
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

// 根据 size 和 mode,创建一个测量要求
public static int makeMeasureSpec(int size, int mode) {
    return size + mode;
}


你可能感兴趣的:(Android View 的绘制流程)