onMeasure被執(zhí)行兩次原理解析
什么情況下會onMeasure會執(zhí)行?
進(jìn)入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:
- 調(diào)用
requestLayout - 調(diào)用
forceRequestLayout
什么時候needsLayout=true:
- 當(dāng)長寬發(fā)生改變
什么時候調(diào)用了onMeasure>方法:
forceLayouy=true- 或者
mMeasureCache沒有當(dāng)前的緩存
總結(jié):
當(dāng)調(diào)用了requestLayout一定會測發(fā)重測過程.當(dāng)forceLayout=false的時候會去判斷mMeasureCache值.現(xiàn)在研究下這個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;
這里一同運(yùn)算就為了排除掉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的構(gòu)造函數(shù)里面會主動賦值一次,然后在ViewGroup.addView時候會給當(dāng)前View的mProvateFlags賦值PFLAG_FORCE_LAYOUT.
為什么onMeasure會被執(zhí)行兩次?
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;
---
}
在第一次觸發(fā)到measure方法時,forceLayoyt=true needsLayout=true,但是layout方法還沒觸發(fā)到.
在第二次觸發(fā)到measure>方法時,forceLayout=true needsLayout=false,所以還是會進(jìn)入onMeasure方法.這次會執(zhí)行layout方法.然后我們在下次的時候forceLayout就等于false了.上面的這一段分析是分析的measure內(nèi)部如何防止多次調(diào)用onMeasure.
分析外部是如何多次調(diào)用measure方法的
在Activity執(zhí)行到onResume生命周期的時候,會執(zhí)行WindowManager.addView操作,WindowManager的具體實現(xiàn)類是WindowManagerImpl然后addView操作交給了代理類WindowManagerGlobal,然后在WindowManagerGlobal的addView里面執(zhí)行了ViewRootImpl.setView操作(ViewRootImpl對象也是在這個時候創(chuàng)建的),在ViewRootImpl會主動調(diào)用一次requestLayout,也就開啟了第一次的視圖 測量 布局 繪制.
在setView的時候主動調(diào)用了一次ViewRootImpl.requestLayout,注意這個requestLayout是ViewRootImpl的內(nèi)部方法,和view viewGroup那些requestLayout不一樣.在ViewRootImpl.requestLayout內(nèi)部調(diào)用了performTraversals方法:
class ViewRootImpl{
void performTraversals(){
if(layoutResuested){
//標(biāo)記1
windowSizeMayChanged |= measureHierarchy(host,lp,res,desiredWindowWidth,desiredWindowHeight);
}
//標(biāo)記2
performMeasure()
performLayout()
}
void measureHierarchy(){
performMeasure()
}
}從ViewRootImpl的執(zhí)行邏輯你可以看出,在執(zhí)行performLayout之前,他自己就已經(jīng)調(diào)用了兩次performMeasure方法.所以你現(xiàn)在就知道為啥了.
以上就是onMeasure被執(zhí)行兩次原理解析的詳細(xì)內(nèi)容,更多關(guān)于onMeasure被執(zhí)行兩次的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android開發(fā)自學(xué)筆記(六):聲明權(quán)限和Activity
這篇文章主要介紹了Android開發(fā)自學(xué)筆記(六):聲明權(quán)限和Activity,本文是上一篇的補(bǔ)充,需要的朋友可以參考下2015-04-04
Android開發(fā)實現(xiàn)的導(dǎo)出數(shù)據(jù)庫到Excel表格功能【附源碼下載】
這篇文章主要介紹了Android開發(fā)實現(xiàn)的導(dǎo)出數(shù)據(jù)庫到Excel表格功能,涉及Android數(shù)據(jù)庫及Excel表格相關(guān)操作技巧,并附帶完整源碼供讀者下載參考,需要的朋友可以參考下2018-03-03
Android6.0開發(fā)中屏幕旋轉(zhuǎn)原理與流程分析
這篇文章主要介紹了Android6.0開發(fā)中屏幕旋轉(zhuǎn)原理與流程,結(jié)合實例形式詳細(xì)分析了Android6.0屏幕旋轉(zhuǎn)的原理與相關(guān)實現(xiàn)流程,并附帶了Android動態(tài)開啟與禁用屏幕旋轉(zhuǎn)的實現(xiàn)方法,需要的朋友可以參考下2017-11-11

