美文网首页
View绘制流程

View绘制流程

作者: 小虫虫奇遇记 | 来源:发表于2020-08-15 12:17 被阅读0次

Window,Activity,ViewRoot 什么关系?
ViewRoot:The top of a view hierarchy, implementing the needed protocol between View and the WindowManager.
DecorView的parent是ViewRootImpl,所有View最上层是ViewRootImpl,实现了ViewParent接口。ViewRootImpl是一个视图层次结构的顶部,它实现了View与WindowManager之间所需要的协议,作为WindowManagerGlobal中大部分的内部实现。在WindowManagerGlobal中实现方法中,都可以见到ViewRootImpl,也就说WindowManagerGlobal方法最后还是调用到了ViewRootImpl。addView,removeView,update调用顺序:
WindowManagerImpl -> WindowManagerGlobal -> ViewRootImpl

在子线程可以更新UI吗一文提到,View的刷新绘制是从onResume中调用ViewRootImpl.setView()方法之后开始进行的。那setView()方法具体进行了什么操作呢?

//ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
           ...
 // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                requestLayout();
            ...
}
   @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

 void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            //同步消息屏障,屏蔽消息队列中后续的同步消息,优先处理异步消息;View刷新操作属于异步消息
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //向底层注册监听屏幕刷新Vsync时钟信号回调,当接收到系统刷新信号时,执行runnble。(最终会在FrameDisplayEventReceiver:: onVsync ,doFrame方法执行该回调)
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
    //mTraversalRunnable是一个请求绘制的runnable对象
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

  final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            //执行绘制开始
            doTraversal();
        }
    }


   void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            //取消同步消息屏障
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

             ...
            performTraversals();
             ...
        }
    }

 private void performTraversals() {
       performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
       performLayout(lp, mWidth, mHeight);
       performDraw();
}

View绘制流程(invalidate方法,有带4个参数的,和不带参数有什么区别;requestLayout触发measure和layout,如何实现局部重新测量,避免全局重新测量问题。)

  • onMeasure:测量视图的大小,从顶层父View到子View递归调用measure()方法,measure()调用onMeasure()方法,onMeasure()方法完成绘制工作。
    MeasureSpec的值由specSize和specMode共同组成的,其中specSize记录的是大小,specMode记录的是规格。specMode一共有三种类型,如下所示:
  1. EXACTLY
    表示父视图希望子视图的大小应该是由specSize的值来决定的,系统默认会按照这个规则来设置子视图的大小,开发人员当然也可以按照自己的意愿设置成任意的大小。
  2. AT_MOST
    表示子视图最多只能是specSize中指定的大小,开发人员应该尽可能小得去设置这个视图,并且保证不会超过specSize。系统默认会按照这个规则来设置子视图的大小,开发人员当然也可以按照自己的意愿设置成任意的大小。
  3. UNSPECIFIED
    表示开发人员可以将视图按照自己的意愿设置成任意的大小,没有任何限制。这种情况比较少见,不太会用到。
  • onLayout:确定视图的位置,从顶层父View到子View递归调用layout()方法,父View将上一步measure()方法得到的子View的布局大小和布局参数,将子View放在合适的位置上。
    layout里面 就是通过serFrame方法设设定本身view的 四个顶点的位置。这4个位置以确定 自己view的位置就固定了
    然后就调用onLayout来确定子元素的位置。
  • onDraw:绘制最终的视图,首先ViewRoot创建一个Canvas对象,然后调用onDraw()方法进行绘制。onDraw()方法的绘制流程为:① 绘制视图背景。② 绘制画布的图层。 ③ 绘制View内容。
    ④ 绘制子视图,如果有的话。⑤ 还原图层。⑥ 绘制滚动条。
  • requestLayout():该方法会递归调用父窗口的requestLayout()方法,直到触发ViewRootImpl的performTraversals()方法,此时mLayoutRequestede为true,会触发onMesaure()与onLayout()方法,不一定会触发onDraw()方法。如果在layout过程中发现l,t,r,b和以前不一样,那就会触发一次invalidate。代码在View的setFrame中,这个会在layout时被调用。
  • invalidate():该方法递归调用父View的invalidateChildInParent()方法,直到调用ViewRootImpl的invalidateChildInParent()方法,最终触发ViewRootImpl的performTraversals()方法,此时mLayoutRequestede为false,不会触发onMesaure()与onLayout()方法,会触发onDraw()方法。
  • postInvalidate():该方法功能和invalidate()一样,只是它可以在非UI线程中调用。

一般说来需要重新布局就调用requestLayout()方法,需要重新绘制就调用invalidate()方法。

相关文章

网友评论

      本文标题:View绘制流程

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