;
? ? ? ? ? ? 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方法。
定位
当view的大小已经设定完毕,则需要确定view在??父view中的位置,也就是把子view放在合理的位置上。因为只有ViewGroup才包含子view,所以一般我们说起父view,肯定是在说ViewGroup。完成布局工作主要分为两部分,也是递归实现的:
1.在layout方法中调用setFrame设置该View视图位于父视图的坐标。
2.如果view是ViewGroup类型,则调用其onLayout方法完成子view布局工作。
下面来看具体源码,父view调用了子view的layout方法:
public void layout(int l, int t, int r, int b) {
? ? ? ? if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
? ? ? ? ? ? onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
? ? ? ? ? ? mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
? ? ? ? }
? ? ? ? int oldL = mLeft;
? ? ? ? int oldT = mTop;
? ? ? ? int oldB = mBottom;
? ? ? ? int oldR = mRight;
? ? ? ? // 判断是否布局是否发生过改变,是否需要重绘。
? ? ? ? boolean changed = isLayoutModeOptical(mParent) ?
? ? ? ? ? ? ? ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
? ? ? ? // 需要重绘。 if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
? ? ? ? ? ? onLayout(changed, l, t, r, b); // 确定view在布局中的位置
? ? ? ? ? ? mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
? ? ? ? ? ? ListenerInfo li = mListenerInfo;
? ? ? ? ? ? if (li != null && li.mOnLayoutChangeListeners != null) {
? ? ? ? ? ? ? ? ArrayList listenersCopy =
? ? ? ? ? ? ? ? ? ? ? ? (ArrayList)li.mOn