04 View的工作原理

2019/12/11 posted in  Android开发艺术探索

4.1 初识ViewRoot和DecorView

  1. ActivityThread#handleResumeActivity
    1. 当 Activity 对象被创建完毕后,会将 DecorView 通过 WindowManager 添加到 Window 中。
  2. WindowManagerGlobal#addView
    1. 创建 ViewRootImpl 对象,并将 ViewRootImpl 对象和 DecorView 建立关联。
  3. ViewRootImpl#setView
    1. ViewRootImpl#requestLayout
    2. ViewRootImpl#scheduleTraversals
    3. ViewRootImpl.TraversalRunnable#performTraversals
      1. ViewRootImpl#performMeasure
      2. ViewRootImpl#performLayout
      3. ViewRootImpl#performDraw

4.2 理解MeasureSpec

4.2.1 MeasureSpec

  • 测量规格,决定View的大小。
  • 是一个32位int值,高2位代表SpecMode(测量模式),低30位代表SpecSize(规格大小)。
  • 三种模式:
    • UNSPECIFIED 父容器不对View有任何限制,要多大就给多大。常用于系统内部。
    • EXACTLY 父容器已经检测出View所需要的精确大小即SpecSize。对应LyaoutParams中的match_parent或具体数值。
    • AT_MOST 父容器为子视图指定一个最大尺寸SpecSize。对应LayoutParams中的wrap_content。
  • View的MeasureSpec由LayoutParams和父容器的LayoutParams共同决定。

4.2.2 MeasureSpec和LayoutParams的对应关系

4.3 View的工作流程

  1. 先将DecorView加载到Window中
  2. 然后开始View的绘制,调用ViewRootImpl的PerformTraversals方法
  3. performTraversals()中依次调用performMeasure()、performLayout()和performDraw()三个方法,分别完成顶级 View的绘制
  4. performMeasure() > measure() > onMeasure(),其中会实现子View的measure过程,layout和draw同理。

4.3.1 measure过程

测量View的宽高

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                         getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
  • View的Measure
    • measure() > onMeasure() > setMeasuredDimension() > getDefaultSize()
  • ViewGroup的Measure
    • measureChildren() > measureChild() > getChildMeasureSpec() > child.measure()

直接继承View的自定义View,需要重写onMeasure()并设置wrap_content时的自身大小,否则效果相当于macth_parent。

如何保证某个View已经测量完毕?

  1. Activity/View#onWindowFocusChanged
  2. view.post(runnable)
  3. ViewTreeObserver
  4. view.measure(int widthMeasureSpec , int heightMeasureSpec)

4.3.2 layout过程

确定元素的位置

  1. View的layout(),通过setFrame()来设定自己的四个顶点,确定自己的位置
  2. onLayout()确定子元素的位置,空方法,不同的ViewGroup实现不同。

4.3.3 draw过程

将View绘制到屏幕上

  1. 绘制背景(drawBackground)
  2. 绘制自己(onDraw)
  3. 绘制children(dispatchDraw)
  4. 绘制装饰(onDrawScrollBars)

4.4 自定义View

4.4.1 自定义View的分类

  1. 继承View
    • 重写onDraw
    • 支持wrap_content、处理padding
  2. 继承ViewGroup
    • 处理自身和子元素的测量和布局
  3. 继承特定的View(如TextView)
  4. 继承ViewGroup(如LinearLayout)

4.4.2 自定义View须知

  1. 尽量不要在View中使用Handler,使用post
  2. 及时停止线程和动画
  3. 滑动嵌套时,处理滑动冲突
  4. 自定义属性
    1. values下创建自定义属性的xml
    2. View的构造方法中解析自定义属性并处理
    3. 在布局中使用自定义属性

4.4.3 自定义View示例

4.4.4 自定义View的思想