两个view:
一个是系统默认的FrameLayout, A
一个是自己自定义的MyView extends View,重载了onMeasure函数(): B
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // TODO Auto-generated method stub
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- // Log.e(TAG, "onMeasure " + MeasureSpec.getSize(widthMeasureSpec)
- // + " " + MeasureSpec.getSize(heightMeasureSpec));
- setMeasuredDimension(MeasureSpec.makeMeasureSpec(mW-=10, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(mH-=10, MeasureSpec.EXACTLY));
- }
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub super.onMeasure(widthMeasureSpec, heightMeasureSpec); // Log.e(TAG, "onMeasure " + MeasureSpec.getSize(widthMeasureSpec) // + " " + MeasureSpec.getSize(heightMeasureSpec)); setMeasuredDimension(MeasureSpec.makeMeasureSpec(mW-=10, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(mH-=10, MeasureSpec.EXACTLY)); }
逻辑很简单,每次只要B的onMeasure被触发,那么B的尺寸就原来小10.
A包含B,
几个测试case: (measure() 是final的,无法覆盖,不过B的measure()应该是每次被调到的)
1. A.requestLayout(); 不会调用到B.onMeasure() 和 B.onLayout(), 会调用B.layout() (注意连draw()都没有被调到,这意味着B被A认为完全没变,不需要重绘)
2. B.forceLayout(); A.requestLayout() 会调用到B的.onMeasure() 和 onLayout() 以及 layout(), draw(), onDraw()
3. B.requestLayout() 会调用到B的.onMeasure() 和 onLayout() 以及 layout(),draw(), onDraw()
4. B.invalidate() B的layout(), onLayout() 和 onMeasure() 没有被调到,只有 draw() 和 onDraw() 被调到.
5 A.invalidate() B的所有函数都不会被调到.
这个结果也符合code的逻辑,forceLayout()如果注释说的一样,是在下一次layout bypass 的过程(自己不会发起一次layout bypass)中,会强制的重新onMeasure和onLayout()
而requestLayout() 除了干forceLayout()的事情将自己的FORCE_LAYOUT标志位设上外,还会发起一次layout pass,
在layout bypass 从 A 传到 B时,虽然 A 的 onMeasure() 和 onLayout() 会调用 B 的 measure() 和 layout(), 但是,因为 B的layout状态没有什么改变,因此
onMeasure()和 onLayout()不会被调用.
在当前4.4的code:
- public void forceLayout() {
- ................................................
- mPrivateFlags |= PFLAG_FORCE_LAYOUT;
- mPrivateFlags |= PFLAG_INVALIDATED;
- }
- public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
- ...............................................................
- if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
- .......................................................
- int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :
- mMeasureCache.indexOfKey(key);
- if (cacheIndex < 0 || sIgnoreMeasureCache) {
- ..................................................
- 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
- setMeasuredDimension((int) (value >> 32), (int) value);
- mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
- }
- ......................................................................................
- mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
- }
- .............................................................................
- }
- public void layout(int l, int t, int r, int b) {
- .....................................................
- if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
- onLayout(changed, l, t, r, b);
- mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
- ...................................................................
- }
- mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
- mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
- }
public void forceLayout() { ................................................ mPrivateFlags |= PFLAG_FORCE_LAYOUT; mPrivateFlags |= PFLAG_INVALIDATED; }public final void measure(int widthMeasureSpec, int heightMeasureSpec) { ............................................................... if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT || ....................................................... int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 : mMeasureCache.indexOfKey(key); if (cacheIndex < 0 || sIgnoreMeasureCache) { .................................................. 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 setMeasuredDimension((int) (value >> 32), (int) value); mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; }...................................................................................... mPrivateFlags |= PFLAG_LAYOUT_REQUIRED; }.............................................................................}public void layout(int l, int t, int r, int b) { ..................................................... if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { onLayout(changed, l, t, r, b); mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED; ................................................................... } mPrivateFlags &= ~PFLAG_FORCE_LAYOUT; mPrivateFlags3 |= PFLAG3_IS_LAID_OUT; }
可见forceLayout()可以导致onMeasure() 和 onLayout(). 而requestLayout() 干的事情比forceLayout()只多不少.
View的forceLayout后面一般会紧跟着View的measure(), 这样可以把view的measuredSize 通过 setMeasuredDimension 设上.
而PFLAG_INVALIDATED 这个flag 应该是标记 重绘的。
而requestLayout()/forceLayout() 设置的另一个flag PFLAG_FORCE_LAYOUT,也一定可以在measure()中 将PFLAG_LAYOUT_REQUIRED flag给打上,
而PFLAG_LAYOUT_REQUIRED 则是可以保证了在调用 layout()函数时,onLayout() 函数会被调用到.
还有一点,之前没有仔细看, View的measure(A, B) 函数:
- 如果有PFLAG_FORCE_LAYOUT 或者 本次的measure的尺寸 A, B 和 之前的尺寸不一样
- if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
- widthMeasureSpec != mOldWidthMeasureSpec ||
- heightMeasureSpec != mOldHeightMeasureSpec) {
- ........................................................
- 这一步其实是从之前的MeasureCache里找是否存在 和 本次的measure尺寸一致的 cache
- int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :
- mMeasureCache.indexOfKey(key);
- 如果没有找到,那么就调用onMeasure, 一般来说,调用了onMeasure才能真正的setMeasuredDimension
- if (cacheIndex < 0 || sIgnoreMeasureCache) {
- // measure ourselves, this should set the measured dimension flag back
- onMeasure(widthMeasureSpec, heightMeasureSpec);
- mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
- } else {
- 如果找到有cache,那么就将cache的值 设为 setMeasuredDimension
- long value = mMeasureCache.valueAt(cacheIndex);
- // Casting a long to int drops the high 32 bits, no mask needed
- setMeasuredDimension((int) (value >> 32), (int) value);
- mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
- }
- }
- ..............................................
- mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
- ..............................................
- mOldWidthMeasureSpec = widthMeasureSpec;
- mOldHeightMeasureSpec = heightMeasureSpec;
- mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
- (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
如果有PFLAG_FORCE_LAYOUT 或者 本次的measure的尺寸 A, B 和 之前的尺寸不一样 if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT || widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec) { ........................................................ 这一步其实是从之前的MeasureCache里找是否存在 和 本次的measure尺寸一致的 cache int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 : mMeasureCache.indexOfKey(key); 如果没有找到,那么就调用onMeasure, 一般来说,调用了onMeasure才能真正的setMeasuredDimension if (cacheIndex < 0 || sIgnoreMeasureCache) { // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } else { 如果找到有cache,那么就将cache的值 设为 setMeasuredDimension long value = mMeasureCache.valueAt(cacheIndex); // Casting a long to int drops the high 32 bits, no mask needed setMeasuredDimension((int) (value >> 32), (int) value); mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } } .............................................. mPrivateFlags |= PFLAG_LAYOUT_REQUIRED; .............................................. mOldWidthMeasureSpec = widthMeasureSpec; mOldHeightMeasureSpec = heightMeasureSpec; mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 | (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension这说明调用measure(A, B), 只要尺寸变化,都会影响 MeasuredHeight/Width
借鉴:http://blog.csdn.net/fyfcauc/article/details/41895509