long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);
if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
widthMeasureSpec != mOldWidthMeasureSpec ||
heightMeasureSpec != mOldHeightMeasureSpec) {
// first clears the measured dimension flag
mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
resolveRtlPropertiesIfNeeded();
int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :
mMeasureCache.indexOfKey(key);
if (cacheIndex < 0 || sIgnoreMeasureCache) {
// measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
} else {
long value = mMeasureCache.valueAt(cacheIndex);
// Casting a long to int drops the high 32 bits, no mask needed
setMeasuredDimensionRaw((int) (value >> 32), (int) value);
mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
// flag not set, setMeasuredDimension() was not invoked, we raise
// an exception to warn the developer
if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
throw new IllegalStateException("onMeasure() did not set the"
+ " measured dimension by calling"
+ " setMeasuredDimension()");
}
mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
}
mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
(long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
}
方法接收的两个参数widthMeasureSpec和heightMeasureSpec表示view的宽高,由上一层父view计算后传递过来。view大小的测量工作在标红的onMeasure方法中进行。我们在自定义view时往往需要重写该方法,根据传入的view大小以及其内容来设定view最终显示的尺寸。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
重写该方法时,我们需要调用setMeasuredDimension这个方法来存储已经测量好的尺寸(这里默认使用getDefalutSize),只有在调用过此方法后,才能通过getMeasuredWidth方法和getMeasuredHeight方法获取到尺寸。同时,我们要保证最后得到的尺寸不小于view的最小尺寸。我们需要注意的是,setMeasuredDimension方法必须在OnMeasure方法中调用,否则会抛出异常。
OK,measure方法至此完毕。然而,我们可以发现真正测量view大小的工作并不在此方法中进行,这里仅仅是一个测量框架,根据各种不同的情况进行判断,完成一些必要的步骤。这些步骤是必须的也是无法被开发者更改的,需要根据情况自定义的工作放在了onMeasure中由开发者完成。这样既保证了绘制流程的执行,又灵活的满足了各种需求,是典型的模板方法模式。
由于一个父view下可能有多个子view,所以measure方法不仅仅执行一次,而是在父view(viewGroup)中获取到所有子view,然后遍历调用子view的measure方法。
定位