0%

View 的工作原理(一):初识 ViewRoot 和 DecorView

1. ViewRoot 的概念

  • ViewRoot 对应于 ViewRootImpl 类,它是连接 WindowManager 和 DecorView 的纽带View 的三大流程均是通过 ViewRoot 来完成的

  • ActivityThread 中,当 Activity 对象被创建完毕后,会将 DecorView 添加到 Window 中,同时会创建 ViewRootImpl 对象,并将 ViewRootImpl 对象和 DecorView 建立关联。源码如下:

    1
    2
    root = new ViewRootImpl(view.getContext(), display);
    root.setView(view, wparams, panelParentView);

2. View 大致的绘制流程

  • View 的绘制流程是从 ViewRoot 的 performTraversal() 方法开始的,经过 measure、layout 和 draw 三个过程最终将一个 View 绘制出来

    • measure: 用来测量 View 的宽高
    • layout: 用来确定 View 在父容器中的布局位置
    • draw: 用来将 View 绘制在屏幕上
  • ViewRoot 的 performTraversal() 方法的工作流程图

    performTraversals() 方法的工作路程图

    1. performTraversals() 会依次调用 performMeasure()performLayout()performdraw() 三个方法,这三个方法分别完成顶级 View 的 measure、layout 和 draw 三大流程
    2. performMeasure() 中会调用 measure 方法,在 measure 方法中又会调用 onMeasure 方法,在 onMeasure 方法则会对所有的子元素进行 measure 过程。接着子元素会重复父容器的 measure 过程,如此反复就完成了整个 View 树的遍历
    3. 同理,performLayout()performDraw() 的传递流程和 performMeasure() 是类似的,唯一不同的是,performDraw() 的传递过程是在 draw 方法中通过 dispatchDraw() 来实现的,不过这并没有本质区别

3. View 三大流程的具体含义

  • measure:

    • measure 过程决定了 View 的宽高
    • measure 完成以后,可以通过 getMeasuredWidth() 方法和 getMeasuredHeight() 方法获取 View 测量后的宽高。在几乎所有的情况下它都等同于 View 最终的宽高,但是特殊情况除外
  • layout:

    • layout 过程决定了 View 的四个顶点的坐标和实际的 View 的宽高
    • layout 完成以后,可以通过 getTop()getBottom()getLeft()getRight() 四个方法来拿到 View 的四个顶点的坐标,并可以通过 getWidth()getHeight() 方法来拿到 View 的最终宽高
  • draw:

    • draw 过程决定了 View 的显示
    • 只有 draw 方法完成以后 View 的内容才能呈现在屏幕上

4. DecorView 的概念

  • DecorView 作为顶级 View,一般情况下它内部会包含一个竖直方向的 LinearLayout,在这个 LinearLayout 里面有上下两个部分(具体情况和 Android 版本以及主题有关),上面时标题栏,下面是内容栏

    • 得到 content: ViewGroup content = (ViewGroup)findViewById(R.android.id.content);
    • 得到 content 中设置的 View: content.getChildAt(0);
  • 分析源码可知,DecorView 其实是一个 FrameLayout,View 层的事件都先经过 DecorView,然后才传递给 View

  • 作为顶级 View 的 DecorView 的结构示意图

DecorView 的结构示意图

-------------------- 本文结束感谢您的阅读 --------------------