onMeasure被執(zhí)行兩次原理解析
什么情況下會(huì)onMeasure會(huì)執(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; } } }
什么時(shí)候forceLayout=true
:
- 調(diào)用
requestLayout
- 調(diào)用
forceRequestLayout
什么時(shí)候needsLayout=true
:
- 當(dāng)長寬發(fā)生改變
什么時(shí)候調(diào)用了onMeasure>
方法:
forceLayouy=true
- 或者
mMeasureCache
沒有當(dāng)前的緩存
總結(jié):
當(dāng)調(diào)用了requestLayout
一定會(huì)測發(fā)重測過程.當(dāng)forceLayout=false
的時(shí)候會(huì)去判斷mMeasureCache
值.現(xiàn)在研究下這個(gè)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
都是緩存上一次的值,那他們有什么不同呢?不同點(diǎn)就是,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
什么時(shí)候mPrivateFlags
會(huì)被賦值PFLAG_FORCE_LAYOUT
.
在view viewGrouup
的構(gòu)造函數(shù)里面會(huì)主動(dòng)賦值一次,然后在ViewGroup.addView
時(shí)候會(huì)給當(dāng)前View
的mProvateFlags
賦值PFLAG_FORCE_LAYOUT
.
為什么onMeasure會(huì)被執(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
方法時(shí),forceLayoyt=true needsLayout=true
,但是layout
方法還沒觸發(fā)到.
在第二次觸發(fā)到measure>
方法時(shí),forceLayout=true needsLayout=false
,所以還是會(huì)進(jìn)入onMeasure
方法.這次會(huì)執(zhí)行layout
方法.然后我們在下次的時(shí)候forceLayout
就等于false
了.上面的這一段分析是分析的measure
內(nèi)部如何防止多次調(diào)用onMeasure
.
分析外部是如何多次調(diào)用measure方法的
在Activity
執(zhí)行到onResume
生命周期的時(shí)候,會(huì)執(zhí)行WindowManager.addView
操作,WindowManager
的具體實(shí)現(xiàn)類是WindowManagerImpl
然后addView
操作交給了代理類WindowManagerGlobal
,然后在WindowManagerGlobal
的addView
里面執(zhí)行了ViewRootImpl.setView
操作(ViewRootImpl
對象也是在這個(gè)時(shí)候創(chuàng)建的),在ViewRootImpl
會(huì)主動(dòng)調(diào)用一次requestLayout
,也就開啟了第一次的視圖 測量 布局 繪制.
在setView
的時(shí)候主動(dòng)調(diào)用了一次ViewRootImpl.requestLayout
,注意這個(gè)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)文章
Flutter給控件實(shí)現(xiàn)鉆石般的微光特效
這篇文章主要給大家介紹了關(guān)于Flutter給控件實(shí)現(xiàn)鉆石般的微光特效的相關(guān)資料,實(shí)現(xiàn)的效果非常不錯(cuò),非常適合大家做開發(fā)的時(shí)候參考,需要的朋友可以參考下2021-08-08Android如何通過命令行操作Sqlite3數(shù)據(jù)庫的方法
這篇文章主要介紹了Android如何通過命令行操作Sqlite3數(shù)據(jù)庫的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06Android實(shí)現(xiàn)單頁面浮層可拖動(dòng)view的一種方法
本篇文章主要介紹了Android實(shí)現(xiàn)單頁面浮層可拖動(dòng)view的一種方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-10-10Android UI設(shè)計(jì)與開發(fā)之PopupWindow仿騰訊新聞底部彈出菜單
這篇文章主要為大家詳細(xì)介紹了Android UI設(shè)計(jì)與開發(fā)之PopupWindow仿騰訊新聞底部彈出菜單,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08Android自定義View實(shí)現(xiàn)驗(yàn)證碼
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)驗(yàn)證碼的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10Android 輕松實(shí)現(xiàn)圖片倒影效果實(shí)例代碼
這篇文章主要介紹了Android 輕松實(shí)現(xiàn)圖片倒影效果實(shí)例代碼,有需要的朋友可以參考一下2014-01-01Android仿今日頭條頂部導(dǎo)航欄效果的實(shí)例代碼
這篇文章主要介紹了Android之仿今日頭條頂部導(dǎo)航欄效果的實(shí)例代碼,具有很好的參考價(jià)值,希望對大家有所幫助,一起跟隨小編過來看看吧2018-05-05Kotlin結(jié)合Rxjava+Retrofit實(shí)現(xiàn)極簡網(wǎng)絡(luò)請求的方法
這篇文章主要給大家介紹了關(guān)于Kotlin結(jié)合Rxjava+Retrofit實(shí)現(xiàn)極簡網(wǎng)絡(luò)請求的相關(guān)內(nèi)容,文中分別對Rxjava和Retrofit進(jìn)行了簡單的介紹,然后通過示例代碼詳細(xì)介紹了如何實(shí)現(xiàn)極簡網(wǎng)絡(luò)請求,需要的朋友可以參考借鑒,下面來一起看看吧。2017-11-11