关于View测量中的onMeasure函数

345 阅读2分钟

在自定义View中我们通常会重写onMeasure,下面来说说这个onMeasure有什么作用 onMeasure主要用于对于View绘制时进行测量

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

setMeasuredDimension是设置它的宽度和高度

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

上面用来获取宽高的函数,可以看出这里有3种模式: UNSPECIFIED 这种模式它的result就是size, size是通过getSuggestedMinimumWidth()和getSuggestedMinimumHeight()获取的, 看看getSuggestedMinimumWidth

protected int getSuggestedMinimumWidth() {
    return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}

通常mBackground是通过外部setBackgroundDrawable调用赋值的. mMinWidth也可以通过外部负责,也可以通过属性赋值

case R.styleable.View_minWidth:
    mMinWidth = a.getDimensionPixelSize(attr, 0);
    break;
public void setMinimumWidth(int minWidth) {
    mMinWidth = minWidth;
    requestLayout();

}

通常UNSPECIFIED是不会调用到的.所以我们无需过度关心它的情况. AT_MOST AT_MOST对于xml属性中的wrap_content, 就是适用view内容的大小,但是自定义view情况设置wrap_content通常会传入填满父控件的specSize。这点从这里可以看出

case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;

这显然不是我们希望看到的,所以自定义需要重写onMeasure,来对应这种情况 EXACTLY EXACTLY模式对应制定的宽高和match_partent, 用来精确的设定宽高. 接下来说说怎么重写onMeasure这个函数,重写onMeasure主要是为了争对设置wrap_content,自定义view填充父容器的情况,所以我们只是争对MeasureSpec.AT_MOST这种情况.

private int measureWidth(int measureSpec){
    int result = 0;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = 200;
            break;
        case MeasureSpec.AT_MOST:
            result = Math.min(result,specSize);
            break;
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
    }
    return result;
}

measureHeight和measureWidth是一样的,注意Math.min(result,specSize)这段代码,用来防止实际父控件的大小比指定的200要小,最终调用:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
}