View工作原理 -- 工作过程 -- measure(1)

View的measure过程

View的measure过程由其measure方法来完成,measure方法是一个final类型的方法,子类不能重写此方法。在View的measure方法中会去调用View的onMeasure方法。

View#onMeasure:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasureDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), 
                        getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

View#getDefaultSize:
public static int getDefaultSize(int size, int measureSpec) {
    int result = size; //size大小由getSuggestedMinimumWidth方法或getSuggestedMinimumHeight方法确定
    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;
}
  1. 当前View的SpecMode为AT_MOSTEXACTLY时,getDefaultSize方法返回的大小就是measureSpec中的specSize,而这个specSize就是View测量后的大小。
  2. 当前View的SpecMode为UNSPECIFIED时,getDefaultSize方法返回的大小就是getSuggestedMinimumWidth方法或getSuggestedMinimumHeight方法的返回值。
    分析getSuggestedMinimumWidth方法:
View#getSuggestedMinimumWidth:
protected int getSuggestedMinimumWidth() {
    return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());

Drawable#getMinimumWidth:
public int getMinimumWidth() {
    final int intrinsicWidth = getIntrinsicWidth();
    return intrinsicWidth > 0 ? intrinsicWidth : 0;
}

(1)如果View没有设置背景,那么View的宽度为mMinWidth。
mMinWidth对应于android:minWidth这个属性所指定的值,如果不指定则默认为0。
(2)如果View设置了背景,那么View的宽度为max(mMinWidth, mBackground.getMinimumWidth())。
Drawable#getMinimumWidth方法返回的是Drawable的原始宽度,前提是这个Drawable有原始宽度,否则就返回0。如ShapeDrawable无原始宽/高,而BitmapDrawable有原始宽/高(图片的尺寸)。

从getDefaultSize方法的实现来看,View的宽/高由SpecSize决定,所有我们可以得出如下结论:直接继承View的自定义控件需要重写onMeasure方法并设置wrap_content时的自身大小,否则在布局中使用wrap_content就相当于使用match_parent。
代码如下:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
    if(widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
        setMeasureDimension(mWidth, mHeight);
    } elss if(widthSpecMode == MeasureSpec.AT_MOST) {
        setMeasureDimension(mWidth, heightSpecSize );
    } elss if(heightSpecMode == MeasureSpec.AT_MOST) {
        setMeasureDimension(widthSpecSize , mHeight);
    }
}

mWidth、mHeight是给View指定的默认的内部宽/高,并在wrap_content时设置此宽/高。对于非wrap_content情形,沿用系统的测量值。
这个默认的内部宽/高可以根据需要灵活指定,例如TextView、ImageView等控件针对wrap_content情形在onMeasure方法均做了特殊处理。

你可能感兴趣的:(View工作原理 -- 工作过程 -- measure(1))