先放上結(jié)論
- requestLayout會(huì)直接遞歸調(diào)用父窗口的requestLayout谱姓,直到ViewRootImpl,然后觸發(fā)peformTraversals,由于mLayoutRequested為true,會(huì)導(dǎo)致onMeasure和onLayout被調(diào)用。不一定會(huì)觸發(fā)OnDraw
- requestLayout觸發(fā)onDraw可能是因?yàn)樵谠趌ayout過程中發(fā)現(xiàn)l,t,r,b和以前不一樣,那就會(huì)觸發(fā)一次invalidate硝桩,所以觸發(fā)了onDraw,也可能是因?yàn)閯e的原因?qū)е耺Dirty非空(比如在跑動(dòng)畫)
- view的invalidate不會(huì)導(dǎo)致ViewRootImpl的invalidate被調(diào)用疚脐,而是遞歸調(diào)用父view的invalidateChildInParent亿柑,直到ViewRootImpl的invalidateChildInParent,然后觸發(fā)peformTraversals棍弄,會(huì)導(dǎo)致當(dāng)前view被重繪,由于mLayoutRequested為false望薄,不會(huì)導(dǎo)致onMeasure和onLayout被調(diào)用,而OnDraw會(huì)被調(diào)用
以上結(jié)論來自 從源碼看invalidate和requestLayout的區(qū)別呼畸,這篇博客講得比較細(xì)致痕支,推薦大家對著源碼看一遍,會(huì)很有收獲蛮原。
博客主要講了
View調(diào)用invalidate方法時(shí)卧须,怎么保證不繪制所有的view,而只繪制當(dāng)前view呢
我這里重點(diǎn)講一下 為什么 View#invalidate
方法不會(huì)導(dǎo)致onMeasure
和onLayout
被調(diào)用儒陨,以及mDirty與onDraw方法的關(guān)系花嘶。這兩個(gè)個(gè)問題被作者一筆帶過了。
1. invalidate不調(diào)用performMeasure和performLayout
先看下 requestLayout 方法
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
//這里設(shè)置為true
mLayoutRequested = true;
scheduleTraversals();
}
}
再來看 performTraversal方法的大致邏輯蹦漠,對應(yīng)的邏輯我都寫到注釋中了椭员,挺清晰的。
private void performTraversals() {
......
// 調(diào)用invalidate的話笛园,mLayoutRequested為false隘击,所以layoutRequested為false
// 調(diào)用requestLayout侍芝,會(huì)把mLayoutRequested設(shè)置為true
boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
......
if (layoutRequested) {
// Clear this now, so that if anything requests a layout in the
// rest of this function we will catch it and re-run a full
// layout pass.
//每次使用performTraversals都會(huì)把mLayoutRequested重置為false
mLayoutRequested = false;
}
// 如果layoutRequested為false,那windowShouldResize一定是false了埋同,不可能可以調(diào)用到performMeasure了
boolean windowShouldResize = layoutRequested && windowSizeMayChange && ......;
if (mFirst || windowShouldResize || ......) {
......
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
......
}
.......
// invalidate沒有把mLayoutRequested設(shè)置為true州叠,因此didLayout將為false,因此也無法調(diào)用performLayout
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
if (didLayout) {
performLayout(lp, mWidth, mHeight);
......
}
......
performDraw()
}
2. performDraw方法與requestLayout凶赁,view.invalidate的關(guān)系
performDraw里面會(huì)調(diào)用draw方法
private boolean draw(boolean fullRedrawNeeded) {
······
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
······
// 執(zhí)行真正的繪制流程
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this, callback);
······
}
······
}
可以看到咧栗,如果!dirty.isEmpty()為true,才會(huì)去繪制虱肄。
而view#invalidate時(shí)楼熄,會(huì)直接把自身的 left,right浩峡,top,bottom傳遞過去错敢,并一路調(diào)用到 ViewRootImpl#invalidateRectOnScreen(Rect dirty)翰灾,該方法中會(huì)調(diào)用scheduleTraversals()
。從而保證可以調(diào)用performDraw()
稚茅。而requestLayout是沒有這一步的纸淮,因此往往不會(huì)執(zhí)行繪制方法。