Android requestLayout与invalidate的区别

先放上结论

  1. requestLayout会直接递归调用父窗口的requestLayout,直到ViewRootImpl,然后触发peformTraversals,由于mLayoutRequested为true,会导致onMeasure和onLayout被调用。不一定会触发OnDraw
  2. requestLayout触发onDraw可能是因为在在layout过程中发现l,t,r,b和以前不一样,那就会触发一次invalidate,所以触发了onDraw,也可能是因为别的原因导致mDirty非空(比如在跑动画)
  3. view的invalidate不会导致ViewRootImpl的invalidate被调用,而是递归调用父view的invalidateChildInParent,直到ViewRootImpl的invalidateChildInParent,然后触发peformTraversals,会导致当前view被重绘,由于mLayoutRequested为false,不会导致onMeasure和onLayout被调用,而OnDraw会被调用

以上结论来自 从源码看invalidate和requestLayout的区别,这篇博客讲得比较细致,推荐大家对着源码看一遍,会很有收获。

博客主要讲了

View调用invalidate方法时,怎么保证不绘制所有的view,而只绘制当前view呢

我这里重点讲一下 为什么 View#invalidate方法不会导致onMeasureonLayout被调用,以及mDirty与onDraw方法的关系。这两个个问题被作者一笔带过了。

1. invalidate不调用performMeasure和performLayout

先看下 requestLayout 方法

public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
          //这里设置为true
        mLayoutRequested = true;
        scheduleTraversals();
    }
}

再来看 performTraversal方法的大致逻辑,对应的逻辑我都写到注释中了,挺清晰的。

    private void performTraversals() {
        ......
                // 调用invalidate的话,mLayoutRequested为false,所以layoutRequested为false
                // 调用requestLayout,会把mLayoutRequested设置为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都会把mLayoutRequested重置为false
                mLayoutRequested = false;
            }
                    // 如果layoutRequested为false,那windowShouldResize一定是false了,不可能可以调用到performMeasure了
            boolean windowShouldResize = layoutRequested && windowSizeMayChange && ......;
            
            if (mFirst || windowShouldResize || ......) {
                    ......
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                ......
            } 
            
            .......
      // invalidate没有把mLayoutRequested设置为true,因此didLayout将为false,因此也无法调用performLayout
            final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
            if (didLayout) {
                performLayout(lp, mWidth, mHeight);
            ......
            }   
            ......
            performDraw()
            }
        

2. performDraw方法与requestLayout,view.invalidate的关系

performDraw里面会调用draw方法

private boolean draw(boolean fullRedrawNeeded) {
    ······
    if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
            ······
            // 执行真正的绘制流程
          mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this, callback);
          ······
    }
    ······
}


可以看到,如果!dirty.isEmpty()为true,才会去绘制。

而view#invalidate时,会直接把自身的 left,right,top,bottom传递过去,并一路调用到 ViewRootImpl#invalidateRectOnScreen(Rect dirty),该方法中会调用scheduleTraversals()。从而保证可以调用performDraw()。而requestLayout是没有这一步的,因此往往不会执行绘制方法。

你可能感兴趣的:(Android requestLayout与invalidate的区别)