美文网首页
Android requestLayout与invalidate

Android requestLayout与invalidate

作者: kirito0424 | 来源:发表于2020-12-20 17:45 被阅读0次

    先放上结论

    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

          本文链接:https://www.haomeiwen.com/subject/djpknktx.html