Android ViewManager解读之requestLayout() 详解

时间:2020-12-07 13:22:54

尊重原创: http://blog.csdn.net/sk719887916/article/details/48464035

上篇《Android View 中invalidate()
你所不知道的那些事
》主要了解了view重绘的整个流程,通过从源码的学习大家了解了view整个invalidate的整个流程,但是上篇中view的另一个重要方法 requestLayout()没做细说,本篇继续分析requestLayout()。

上文中细说了从view.invalidate()开始到view.draw()的整个过程,当然今天也从view.requestLayout()开始分析起。

requestLayout过程

顾名思义,本代码从方法名字理解为请求布局,此方法主要是将本view重新布局 ,然后绘制。当我们从某个view中调用 view.requestLayout()时,会触发super的requestLayout(),也就是整个View的invalidate(),经过判断后响应ViewParent的requestLayout(),接着获取ViewRootImpl实列调用其本身的requestLayout()
,通过执行scheduleTraversals();发送消息 ,然后ViewRoot的hander处理performTraversals(),从而做出重绘draw()动作 ,,最终还是和invalidate()执行到同一个方法上,现在我就从源码分析起

1  TextView.requestLayout()

具体某个控件 可以调用此方法,则可以让本View重新走一边测量,布局,以及绘制流程,他会调用父类的(也就是View)的requestLayout()方法,可以看下如下代码 (EP1),代码逻辑也不复杂,只要是清空测量好的数据,接着重新设置下view的AttachInfo,然后最后调用Parent的requsetLayout(),把请求权交给ViewGroup处理,

而ViewGroup呢 会得到我们的View的控制核心ViewRoot,用他的实现类ViewRootImpl去重新requestLayout()。

EP1:

 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()) {
            mParent.requestLayout();
        }
        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
            mAttachInfo.mViewRequestingLayout = null;
        }
    }

当然这种请求就是一种递归的过程, 在上面的mParent.requestLayout()其实他只是个接口,最终还是由View重载实现,还是回到上面的方法中,当ViewRoot已经请求过了,标记为不再请求布局时,才会return,可见上面的 第八行代码。

2 ViewRoot. requestLayout()

当View把控制权交由ViewRoot后,ViewRoot就会调用自己的requestLayout(),代码如下 (EP2),首先会检测修改UI的线程是否是UI主线程,当然这个代码在上篇的文章中已经说过,子线程无法更改UI的异常就是从 checkThread()里面抛出来的,接着讲本此操作标记为已经请求过,后调用 scheduleTraversals()发送消息给我们的viewRoot处理消息。

EP2:

/**
     * {@inheritDoc}
     */
    public void requestLayout() {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }

3   scheduleTraversals( )

从上篇文章大家都知道ViewRoot本省就是handler的子类,本身就可以发送消息和处理消息,我们再来看下scheduleTraversals()(EP3),方法内部很简单,标记为已经发送了该消息,然后sendMeasse,带的参数也就是说明要执行Traversals()了,那么我再继续看看在哪里去处理这条消息的呢,

EP3:

  public void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            sendEmptyMessage(DO_TRAVERSAL);
        }
    }

4 performTraversals( )

接着找到在handleMessage中处理了此消息,下面代码(Ep4)中的第二个case,就会处理我们请求执行的地方,发现也是和invalidate()执行一样的结果,在这里估计你也知道了,其实invalidfate()和resquestLayout()最终都会被ViewRoot在performTraversals()去处理,而这方法在 上篇《Android
View 中invalidate() 你所不知道的那些事
》 中做了细说,最后还是会调用我们需要view注入进去的那个view的draw()函数,比如就是上文中TextView的darw()的方法, 这让我们更见确信Android中的view都是从子节点绘制逐步网上层递归的规程,然而开发的时候确实从父节点开始布局的,但是这不是互相矛盾的。当我代码加入一个view时,他不会先对View所在的viewGruop做准备工作,而是先对这个view进行处理,不管是measure,还是layout,都是一样的原理,直到我们的根节点被标记完所有子节点都进行完了测量,布局,绘制后
ViewRoot才会回调当前界面的view全部绘制结束,

EP4:

@Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        case View.AttachInfo.INVALIDATE_MSG:
            ((View) msg.obj).invalidate();
            break;
        case View.AttachInfo.INVALIDATE_RECT_MSG:
            final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj;
            info.target.invalidate(info.left, info.top, info.right, info.bottom);
            info.release();
            break;
        case DO_TRAVERSAL:
            if (mProfile) {
                Debug.startMethodTracing("ViewRoot");
            }

            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
            break;
        case FINISHED_EVENT:
            handleFinishedEvent(msg.arg1, msg.arg2 != 0);
            break;

在performTraversals()的方法最后也会发现 如下代码,告知我们的window,此view已经全部绘制完毕。

             sWindowSession.finishDrawing(mWindow);

总结:

看了两篇,终于可以做总结了,还是很多疑问没有解开,那到底invalidate()和requestLayout()有什么区别,当我们通过源码分析得出不管哪个方法都会执行draw(),那么上篇中我也做了细说invalidate(),invalidate()也会执行我们的onmeasure(),layout(),draw(),但是它和resquestLayout()的不同之处,他不会清空我们测量的缓存数据,当然也不会重新回调measure,但是他会重新走Layout()过程,最终他们的目的都是重新绘制view,但是细微区别,一个要进行重新测量,一个无需进行测量。