美文网首页
Android View requestLayout源码分析

Android View requestLayout源码分析

作者: wanderingGuy | 来源:发表于2017-08-24 00:15 被阅读0次

    我们从view的requestLayout方法开始分析。

    public void requestLayout() {
        if (mMeasureCache != null) mMeasureCache.clear();
    
        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
            // Only trigger request-during-layout logic if this is the view requesting it,
            // not the views in its parent hierarchy
            ViewRootImpl viewRoot = getViewRootImpl();
            if (viewRoot != null && viewRoot.isInLayout()) {
                if (!viewRoot.requestLayoutDuringLayout(this)) {
                    return;
                }
            }
            mAttachInfo.mViewRequestingLayout = this;
        }
    
        mPrivateFlags |= PFLAG_FORCE_LAYOUT;
        mPrivateFlags |= PFLAG_INVALIDATED;
    
        if (mParent != null && !mParent.isLayoutRequested()) {
            //核心代码 调用父view的requestLayout方法
            mParent.requestLayout();
        }
        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
            mAttachInfo.mViewRequestingLayout = null;
        }
    }
    

    viewParent的实例就是View树的ViewRootImpl对象,因此如果在回溯view树过程中没有中断,将会调用它的requestLayout方法。

    # ViewRootImpl
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();//核心代码
        }
    }
    
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //这里需要关注这个runnable对象
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
    
    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
    
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
    
            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }
    
            performTraversals();//遍历起点
    
            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }
    
    //这个方法太长了 只调出重点
    private void performTraversals() {
        // cache mView since it is used so much below...
        final View host = mView;
        /** View树遍历的主要三个步骤measure、layout、draw **/  
        ......  
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);  
        ......  
        performLayout(lp, desiredWindowWidth, desiredWindowHeight);  
        ......  
        performDraw();  
    }
    

    performMeasure、performLayout、performDraw内部依次调用view的绘制流程方法。

    mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); 
    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());  
    draw(fullRedrawNeeded); 
    

    划重点了,这里的mView就是此ViewRootImpl管理的DecorView。
    也就是说调用measure、layout、draw就会触发这个view树的测量、布局和重绘。这部分内容看参看一篇不错的文章
    Android视图框架Activity,Window,View,ViewRootImpl理解

    何时会触发view树的遍历呢?

    • View.requestLayout
    • View.setLayoutParams
    • View.requestFocus(请求view树的draw过程,但只绘制需要重绘的视图,最终是调用invalidate重绘)

    注意这里invalidate()方法虽然最终会调用到performTraversals()方法中,但这时measure和layout流程是不会重新执行的,因为视图没有强制重新测量的标志位,而且大小也没有发生过变化,所以这时只有draw流程可以得到执行。而如果你希望视图的绘制流程可以完完整整地重新走一遍,就不能使用invalidate()方法,而应该调用requestLayout()了。

    一般引起invalidate()操作的函数如下:

    1. 直接调用invalidate()方法,请求重新draw(),但只会绘制调用者本身。
    2. setSelection():请求重新draw(),但只会绘制调用者本身。
    3. setVisibility() :当View可视状态在INVISIBLE转换VISIBLE时,会间接调用invalidate()方法,继而绘制该View。
      当View的可视状态在INVISIBLE/ VISIBLE 转换为GONE状态时,会间接调用requestLayout() 和invalidate方法。
      同时,由于整个个View树大小发生了变化,会请求measure()过程以及draw()过程,同样地,只绘制需要“重新绘制”的视图。
    4. setEnabled()方法: 请求重新draw(),但不会重新绘制任何视图包括该调用者本身。
    5. dispatchAppVisibility:当应用程序的可见性发生改变时(如启动一个新的Activity),会调用这个函数;一旦ViewRootImpl受到Visibility改变的消息,就会组织一次遍历。

    相关文章

      网友评论

          本文标题:Android View requestLayout源码分析

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