UI 绘制流程(测量、布局、绘制)
View = 模式 + 尺寸 -> MeasureSpec 32位 int 值
00,0000000000,0000000000,0000000000
MODE_MASK:11,0000000000,0000000000,0000000000
~MODE_MASK:00,1111111111,1111111111,1111111111
SpecMode(前2位) + SpecSize(后30位)
public static final int UNSPECIFED = 0 << MODE_SHIFT; // 00,0000000000,0000000000,0000000000
父容器不对 View 做任何限制,系统内部使用
public static final int EXACTLY = 0 << MODE_SHIFT; // 01,0000000000,0000000000,0000000000
父容器检测出 View 的大小,View 的大小就是 SpecSize:LayoutParams -> match_parent or 固定大小
public static final int AT_MOST = 0 << MODE_SHIFT; // 10,0000000000,0000000000,0000000000
父容器指定一个可用大小,View 的大小不能超过这个值,LayoutParams -> wrap_content
mode + size -> MeasureSpec
MeasureSpec = mode + size
ViewGroup:measure -> onMeasure(测量子控件的宽高) -> setMeasureDimension -> setMeasureDimensionRaw(保存自己的宽高)
View:measure -> onMeasure -> setMeasureDimension -> setMeasureDimensionRaw(保存自己的宽高)
View 的测量-确定 DecorView 的 MeasureSpec
DecorView 的 MeasureSpec 由窗口大小和自身 LayoutParams 决定,遵守如下规则:
- LayoutParams.MATCH_PARENT:精确模式,窗口大小
- LayoutParams.WRAP_CONTENT:最大模式,最大为窗口大小
- 固定大小:精确模式,大小为 LayoutParams 的大小
View 的 MeasureSpec 由父容器的 MeasureSpec 和自身 LayoutParams 决定
childLayoutParams\parentSpecMode | EXACTLY | AT_MOST | UNSPECIFED |
---|---|---|---|
dp/px | EXACTLY childSize | EXACTLY childSize | EXACTLY childSize |
match_parent | EXACTLY parentSize | EXACTLY parentSize | UNSPECIFED 0 |
wrap_content | AT_MOST parentSize | AT_MOST parentSize | UNSPECIFED 0 |
View 的布局
- 调用 View.layout 确定自身的位置,即确定 mLeft,mTop,mRight,mBottom 的值。
- 如果是 ViewGroup 类型,需要调用 onLayout 确定子 View 的位置。
ViewGroup:layout(来确定自己的位置,4个点的位置) -> onLayout(进行子 View 的布局)
View:layout(来确定自己的位置,4个点的位置)
View 的绘制
- 绘制背景 drawBackground(canvas)
- 绘制自己 onDraw(canvas)
- 绘制子 View dispatchDraw(canvas)
- 绘制前景,滚动条等装饰 onDrawForground(canvas)
自定义 View 的流程
onMeasure -> onLayout(容器) -> onDraw(可选)