什么情况下会onMeasure
会履行?
进入View
的measure
办法:
void measure(){
boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
boolean specChanged = widthMeasureSpec != mOldWidthMeasureSpec
|| heightMeasureSpec != mOldHeightMeasureSpec;
boolean isSepcExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY
&& MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY;
boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec)
&& getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec);
final boolean needsLayout = specChanged
&& (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize);
if(forceLayout || needLayout){
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
if (cacheIndex < 0 || sIgnoreMeasureCache) {
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
} else {
long value = mMeasureCache.valueAt(cacheIndex);
setMeasuredDimensionRaw((int) (value >> 32), (int) value);
mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
}
}
什么时分forceLayout=true
:
- 调用
requestLayout
- 调用
forceRequestLayout
什么时分needsLayout=true
:
- 当长宽发生改动
什么时分调用了onMeasure>
办法:
forceLayouy=true
- 或者
mMeasureCache
没有当时的缓存
所以总结:当调用了requestLayout
一定会测发重测进程.当forceLayout=false
的时分会去判别mMeasureCache
值.现在研讨下这个mMeasureCache
class View{
LongSparseLongArray mMeasureCache;
void measure(widthSpec,heightSpec){
---
long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
if(cacheIndex<0){
onMeasure(widthSpec,heightSpec);
}
mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
mMeasureCache.put(key,widhSpec|heightSpec);
---
}
}
这里能够看到oldWidthMeasureSpec
和mMeasureCache
都是缓存上一次的值,那他们有什么不同呢?不同点便是,oldWidthMeasureSpec>
不仅仅缓存了丈量的spec
模式并且缓存了size
.可是mMeasureCache
只缓存了size
.从这行代码能够看出:
long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
这里一同运算就为了排除去spec
造成的影响.
//不信你能够试下下面的代码
public class Test {
public static void main(String[] args) {
long widthMeasureSpec = makeMeasureSpec(10,0);
long heightMeasureSpec = makeMeasureSpec(20,0);
long ss = widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
System.out.println("=========="+ss);
}
private static final int MODE_MASK = 0x3 << 30;
public static int makeMeasureSpec(int size,
int mode) {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
//42949672980
//42949672980
//42949672980
什么时分mPrivateFlags
会被赋值PFLAG_FORCE_LAYOUT
.
在view viewGrouup
的结构函数里面会自动赋值一次,然后在ViewGroup.addView
时分会给当时View
的mProvateFlags
赋值PFLAG_FORCE_LAYOUT
.
为什么onMeasure
会被履行两次?
void measure(int widthMeasureSpec,int heightMeasureSpec){
----
boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
if(forceLayout | needsLayout){
onMeasure()
}
----
}
public void layout(int l, int t, int r, int b){
---
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
---
}
在第一次触发到measure
办法时,forceLayoyt=true needsLayout=true
,可是layout
办法还没触发到.
在第2次触发到measure>
办法时,forceLayout=true needsLayout=false
,所以仍是会进入onMeasure
办法.这次会履行layout
办法.然后我们在下次的时分forceLayout
就等于false
了.上面的这一段剖析是剖析的measure
内部怎么防止屡次调用onMeasure
.
现在剖析外部是怎么屡次调用measure
办法的:
在Activity
履行到onResume
生命周期的时分,会履行WindowManager.addView
操作,WindowManager
的具体完成类是WindowManagerImpl
然后addView
操作交给了署理类WindowManagerGlobal
,然后在WindowManagerGlobal
的addView
里面履行了ViewRootImpl.setView
操作(ViewRootImpl
目标也是在这个时分创建的),在ViewRootImpl
会自动调用一次requestLayout
,也就开启了第一次的视图 丈量 布局 绘制.
在setView
的时分自动调用了一次ViewRootImpl.requestLayout
,注意这个requestLayout
是ViewRootImpl
的内部办法,和view viewGroup
那些requestLayout
不一样.在ViewRootImpl.requestLayout
内部调用了performTraversals
办法:
class ViewRootImpl{
void performTraversals(){
if(layoutResuested){
//符号1
windowSizeMayChanged |= measureHierarchy(host,lp,res,desiredWindowWidth,desiredWindowHeight);
}
//符号2
performMeasure()
performLayout()
}
void measureHierarchy(){
performMeasure()
}
}
从ViewRootImpl
的履行逻辑你能够看出,在履行performLayout
之前,他自己就已经调用了两次performMeasure
办法.所以你现在就知道为啥了.