android View.measure()初探

部分源代码:

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {  
    if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||  
            widthMeasureSpec != mOldWidthMeasureSpec ||  
            heightMeasureSpec != mOldHeightMeasureSpec) {  
  
        // first clears the measured dimension flag  
        mPrivateFlags &= ~MEASURED_DIMENSION_SET;  
  
        if (ViewDebug.TRACE_HIERARCHY) {  
            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);  
        }  
  
        // measure ourselves, this should set the measured dimension flag back  
        onMeasure(widthMeasureSpec, heightMeasureSpec);  
  
        // flag not set, setMeasuredDimension() was not invoked, we raise  
        // an exception to warn the developer  
        if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {  
            throw new IllegalStateException("onMeasure() did not set the"  
                    + " measured dimension by calling"  
                    + " setMeasuredDimension()");  
        }  
  
        mPrivateFlags |= LAYOUT_REQUIRED;  
    }  
  
    mOldWidthMeasureSpec = widthMeasureSpec;  
    mOldHeightMeasureSpec = heightMeasureSpec;  
}  

1.检查传入的widthMeasureSpec和heightMeasureSpec是否与当前的值是一样的,不一样的话,调用onMeasure函数,并设置mPrivateFlags。

2.保存新值到mOldWidthMeasureSpec和mOldHeightMeasureSpec。这两个变量不用深究了,没有其他地方用到,就只是在这个函数中用来比较值用的。

3.这里判断符合条件后会抛出一个IllegalStateException的异常,它的提示信息很清楚,告诉我们要调用setMeasuredDimension()方法。但到底是怎么回事呢?这是在你需要重写onMeasure函数时需要注意的。如果没有调用这个方法就会抛出异常。

接下来是默认的onmeasure()部分源代码:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),  
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));  
}  
getSuggestedMinimumWidth()和getSuggestedMinimumHeight()是一个默认的最小宽或高,setMeasureDimension()源代码:

protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {  
    mMeasuredWidth = measuredWidth;  
    mMeasuredHeight = measuredHeight;  
  
    mPrivateFlags |= MEASURED_DIMENSION_SET;  
}  
这个方法只是一个简单的赋值,获取宽高的操作在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;  
}  


在这里我们可以得知如果spcMode为MeasureSpec.UNSPECIFIED,则返回默认的最小宽或高,如果为MeasureSpec.AT_MOST和MeasureSpec.EXACTLY则从我们传入的参数measureSpec中提取出来的specSize被采用了。

MeasureSpec.AT_MOST是最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。

MeasureSpec.EXACTLY是精确尺寸,当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width="50dip",或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。

最后来看MeasureSpec类的部分源码:

public static class MeasureSpec {  
  
    private static final int MODE_SHIFT = 30;  
    private static final int MODE_MASK  = 0x3 << MODE_SHIFT;  
    public static final int UNSPECIFIED = 0 << MODE_SHIFT;  
    public static final int EXACTLY     = 1 << MODE_SHIFT;  
    public static final int AT_MOST     = 2 << MODE_SHIFT;  
  
    public static int makeMeasureSpec(int size, int mode) {  
        return size + mode;  
    }  
  
    public static int getMode(int measureSpec) {  
        return (measureSpec & MODE_MASK);  
    }  
  
    public static int getSize(int measureSpec) {  
        return (measureSpec & ~MODE_MASK);  
    }  
}  
我们可以看到MeasureSpec类提供了三种模式和MODE_MASK,分别是:MODE_MASK:11(30个0),UNSPECIFIED:00(30个0),EXACTLY:01(30个0),AT_MOST:10(30个0),通过与这三种模式和MODE_MASK的与,非运算将传进来的int参数中的MODE和SIZE分离开来。

简单示例:
下面是一个调用measure方法的示例:

mTextView.measure(MeasureSpec.EXACTLY + mTextView.getWidth(), MeasureSpec.EXACTLY);  
mTextView.layout(0, 0, mTextView.getMeasuredWidth(), mTextView.getMeasuredHeight()); 

把mode标志和你想设置的大小相加,传进去就OK啦。这里设置height的时候我是想设0,因此直接传了MeasureSpec.EXACTLY进去。

当然,measure完后,并不会实际改变View的尺寸,需要调用View.layout方法去进行布局。按示例调用layout函数后,View的大小将会变成你想要设置成的大小。









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值