目录

一、基础知识
在学习View的工作原理之前,需要先学习一些基本的概念。
2.1 ViewRoot
Q1: ViewRoot是什么?
- 对应
ViewRootImpl类。- 连接
WindowManagerService和DecorView的纽带。
Q2:ViewRoot在View绘制中有什么作用?
- View的三大流程(测量(
measure),布局(layout),绘制(draw))均通过ViewRoot来完成。
注意:ViewRoot并不属于View树的一份子。从源码实现上来看,它既非View的子类,也非View的父类,但是,它实现了ViewParent接口,这让它可以作为View的名义上的父视图。
View的绘制流程从ViewRoot的performTraversals开始,如图。
onMeasure方法会对所有子元素进行measure过程,在measure方法中又会调用onMeasure方法,如此反复最终完成整个View树的遍历,layout,draw方法同理。

2.2 DecorView
Q1:DecorView是什么?
包括两部分,标题栏和内容栏,如图。
DecorView是FrameLayout的子类,它可以被认为是Android视图树的根节点视图。setContentView所布置的文件是被加入内容栏的。
Q: DecorView在View绘制中有什么作用?
View层的事件都需先经过
DecorView,再传递给View,分发的过程在View体系详解有讲到。

2.3 ViewGroup.LayoutParams
此部分参考自:自定义View Measure过程 - 最易懂的自定义View原理系列(2)
Q1:怎么理解ViewGroup.LayoutParams?
ViewGroup的子类(RelativeLayout、LinearLayout)有其对应的ViewGroup.LayoutParams子类如:
RelativeLayout的ViewGroup.LayoutParams子类 =RelativeLayoutParams
Q2: 这个类有什么作用?
指定视图
View的高度(height)和 宽度(width)等布局参数。
Q3:怎么使用?
| 参数 | 解释 |
|---|---|
| 具体值 | dp / px |
| fill_parent | 强制性使子视图的大小扩展至与父视图大小相等(不含 padding ) |
| match_parent | 与fill_parent相同,用于Android 2.3 & 之后版本 |
| wrap_content | 自适应大小,强制性地使视图扩展以便显示其全部内容(含 padding ) |
2.4 MeasureSpec
定义:测量规格类。
组成:测量规格
(MeasureSpec)= 测量模式(mode)(高2位) + 测量大小(size)(低30位)。作用:通过宽测量值
widthMeasureSpec和高测量值heightMeasureSpec决定View的大小。
Q1:为什么说是很大程度决定了View的尺寸规格?
答:View的尺寸规格还受父容器影响,因为父容器影响View的MeasureSpec的创建过程。
Q2:MeasureSpec有几种模式?
测量模式
(Mode)的类型有3种:UNSPECIFIED、EXACTLY 和
AT_MOST。

2.4.1 MeasureSpec值的计算
View: 取决于View的布局参数(
LayoutParams)和父容器的MeasureSpec值。顶级View: 取决于自身布局参数 和窗口尺寸。



二、View的工作流程
以下流程图的方法为源码中的方法,感兴趣的读者可以自行查看源码,强推Carson_Ho博客,内有详细解读。
2.1 measure
作用:测量View的宽/高。
注意:某些情况需要多次
measure才能确定View的宽高,此时测试的结果不准确,建议在layout过程中onLayout()获取最终宽/高。
measure测量有两种情况:
- 单一
View ViewGroup

2.1.1 View

说明:
Measure过程中,主要目的就是为了测量出View的宽/高,在measure()入口方法中调用了onMeasure(),而在onMeasure方法中,调用了getDefaultSize()得出测量后View的宽/高,再调用setMeasureDimension()存储测量后的宽高。测量过程到此结束,结果是存储了一个测量后的宽高。
Q1:measure流程最后使用的是getDefaultSize()得出的宽/高,那么这个宽/高是什么?
-
AT_MOST和EXACTLY:
getDefaultSize()返回的大小是measureSpec中的specSize,这个specSize就是最终的测量结果。 -
UNSPECIFIED:
-
有背景
宽/高为
android:minWidth属性所指定的值,若无指定,则为0。 -
无背景
View的宽/高度为android:minWidth/android:minHeight属性所指定的值和mBackground.getMinimumWidth()/mBackground.getMinimumHeight()中的最大值。

-
2.1.2 ViewGroup
ViewGroup除了完成自己的measure过程之外,还会遍历所有子View的measure方法。
ViewGroup是一个抽象类,没有重写View的onMeasure方法(自定义View时需要自己实现)。提供了一个
measureChildren方法。

说明:在
ViewGroup的measure过程中,先在入口measure()内调用onMeasure(),与View中的onMeasure不同,ViewGroup中没有实现这个方法,因为不同的ViewGroup子类(LinearLayout、RelativeLayout/ 自定义ViewGroup子类等)具备不同的布局特性,这导致他们子View的测量方法各有不同,故需自己重写。在这个方法中包含了三个方法
measureChildren()系统方法,遍历子View 并且调用
measureChild()进行下一步测量。
measureCarson()需要自己重写,合并所有子View的尺寸大小,最终得到
ViewGroup父视图的测量值。
setMeasureDimension()与单一View一样,存储测量后的数据。
2.2 layout
作用:确定View的位置。

2.2.1 View

说明:
由于单一View是没有子View的,故o
nLayout()是一个空实现。由于在
layout()中已经对自身View进行了位置计算,所以单一View的layout过程在layout()后就已完成了。
2.2.2 ViewGroup

说明:
Viewgroup在onLayout方法中遍历了子View,调用child.layout(),计算每个子View的位置,一开始计算ViewGroup位置时,调用的是ViewGroup的layout()和onLayout(),当遍历子View计算子View位置时,调用的是子View的layout()和onLayout()。
2.3 draw
作用:将View绘制到屏幕上面。

2.3.1 View

说明:所有的视图最终都是调用 View 的 draw ()绘制视图。
2.3.1 ViewGroup

三、自定义View
3.1 自定义View的分类
- 继承View重写
onDraw方法- 主要用于实现一些不规则的效果。
- 重写
onDraw方法,需要支持wrap_content并且需要自己处理padding。
- 继承
ViewGoup派生特殊的Layout(除了系统布局,重新定义一种新布局)- 实现自定义布局。
- 需要合适的处理
ViewGroup的测量,布局两个过程,并同时处理子元素的测量和布局过程。
- 继承特定的
View- 扩展某些已有的View的功能。
- 容易实现,不需要自己支持wrap_content和padding。
- 继承特定的
ViewGroup- 常见,第二种情况实现的也能用这种方式实现。
- 与第二种的区别就是不用自己处理
ViewGroup的测量和布局,第二种更接近底层。
3.2 自定义View须知
- 让View支持wrap_content
- 让View支持padding
- 不要在View中使用Handler
- View内部本身有post系列的方法,可以完全替代Handler的作用。
- View中如果有线程或动画,需要及时停止
- 与生命周期同步,不然会造成内存泄漏
- 处理滑动冲突
参考自:
- 《Android开发艺术探索》
- 《Android进阶之光》
- 自定义View Measure过程 - 最易懂的自定义View原理系列(2)
- 进阶之路 | 奇妙的View之旅




