其他
View为什么会至少进行2次onMeasure、onLayout
https://www.jianshu.com/p/733c7e9fb284
http://blog.csdn.net/guolin_blog/article/details/44996879
View自身进行了2次onMeasure、onLayout ViewGroup对Child进行了2次measure、layout 我们知道View的绘制流程都始于ViewRootImpl的performTraversals方法,有理由怀疑performTranversals执行了2次。
按照上面的猜想,先进入View类查找总共就2处调用了onMeasure方法如下:
.........
final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
.........
int cacheIndex = forceLayout ? -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;
}
.........
}
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
..........
}
/**
* Flag indicating that a call to measure() was skipped and should be done
* instead when layout() is invoked.
*/
static final int PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT = 0x8;
//为true则会执行else语句,所以第一次执行并不会执行 performDraw方法,即View的onDraw方法不会得到调用
//第二次执行则为false,并未创建新的Surface,第二次才会执行 performDraw方法
if (!cancelDraw && !newSurface) {
if (!skipDraw || mReportNextDraw) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
performDraw();
}
} else {
//2.viewVisibility是wm.add的那个View的属性,View的默认值都是可见的
if (viewVisibility == View.VISIBLE) {
// Try again
//3.再执行一次 scheduleTraversals,也就是会再执行一次performTraversals
scheduleTraversals();
} else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).endChangingAnimations();
}
mPendingTransitions.clear();
}
}
既然确定了performTravelsals会执行2次,那么肯定会执行2次measure方法,但是执行2次measure方法就一定会执行2次onMeasure方法吗?
答案是否定,分析过View measure方法源码的都应知道measure方法做了2级测量优化:
如果flag不为forceLayout或者与上次测量规格(MeasureSpec)相比未改变,那么将不会进行重新测量(执行onMeasure方法),直接使用上次的测量值; 如果满足非强制测量的条件,即前后二次测量规格不一致,会先根据目前测量规格生成的key索引缓存数据,索引到就无需进行重新测量;如果targetSDK小于API 20则二级测量优化无效,依旧会重新测量,不会采用缓存测量值。
.........
//mPrivateFlags第16位设置为0,0表示不强制layout
//PFLAG_FORCE_LAYOUT = 0x00001000
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}
第一次performTranversals会执行2次performMeasure:先执行measureHierarchy方法中的performMeasure方法。
这就能解释为什么前2次测量都执行了onMeasure方法,而未采用测量优化策略,因为前2次performMeasure并未经过performLayout,也即forceLayout的标志位一直为true,自然不会取缓存优化。理论上第三次测量经过第一次performTranversals中的performLayout,强制layout的flag应该为false,然而实际上却又变成了true,至于在哪儿恢复为true的,我在源码中并没有找到答案。
但是这在api24、25上却及其符合我们的推论,第三次强制layout的flag为false,即第二次performTranversals并不会导致View的onMeasure方法的调用,由于未调用onMeasure方法,也不会调用onLayout方法,即api 25只会执行2次onMeasure、一次onLayout、一次onDraw,如下图所示:
api25-24:执行2次onMeasure、2次onLayout、1次onDraw,理论上执行三次测量,但由于测量优化策略,第三次不会执行onMeasure。
api23-21:执行3次onMeasure、2次onLayout、1次onDraw,forceLayout标志位,离奇被置为true,导致无测量优化。
api19-16:执行2次onMeasure、2次onLayout、1次onDraw,原因第一次performTranversals中只会执行measureHierarchy中的performMeasure,forceLayout标志位,离奇被置位true,导致无测量优化。
总之,造成这个现象的根本原因是performTranversal函数在View的测量流程中会执行2次。