Android requestLayout與invalidate的區(qū)別

先放上結(jié)論

  1. requestLayout會(huì)直接遞歸調(diào)用父窗口的requestLayout,直到ViewRootImpl,然后觸發(fā)peformTraversals,由于mLayoutRequested為true,會(huì)導(dǎo)致onMeasure和onLayout被調(diào)用。不一定會(huì)觸發(fā)OnDraw
  2. 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)畫)
  3. 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ì)致,推薦大家對(duì)著源碼看一遍,會(huì)很有收獲。

博客主要講了

View調(diào)用invalidate方法時(shí),怎么保證不繪制所有的view,而只繪制當(dāng)前view呢

我這里重點(diǎn)講一下 為什么 View#invalidate方法不會(huì)導(dǎo)致onMeasureonLayout被調(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方法的大致邏輯,對(duì)應(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í)行繪制方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容