Android View 的绘制流程(二)
承接上篇文章Android View的绘制流程(一)
这篇承接上篇,主要说下View绘制流程的具体细节核心方法
下面依次分析
其实的一系列操作,只做了一件事,就是赋值和,就是确定控件的测量宽高,在测量确定宽高的时候,我们要理解一个很重要的概念:
= 模式 + 尺寸
MeasureSpec 是对View宽高属性的封装,是一个32位的int值,前两位表示模式,后30位表示尺寸 及SpecModel+SpecSize
View中定义了三个SpecModel常量,名字和含义分别为:
public static final int UNSPECIFIED = 0 << MODE_SHIFT; 00000000000000000000000000000000
父容器不对View做任何限制,一般是系统内部使用
public static final int EXACTLY = 1 << MODE_SHIFT; 01000000000000000000000000000000
父容器检测出View的大小,View的大小就是SpecSize 对应布局属性LayoutPamras match_parent以及固定大小
public static final int AT_MOST = 2 << MODE_SHIFT; 10000000000000000000000000000000
父容器指定一个可用大小,View的大小不能超过这个值,LayoutPamras wrap_content
ViewGroup和View测量确定宽高的流程:
ViewGroup measure --> onMeasure(测量子控件的宽高)--> setMeasuredDimension -->setMeasuredDimensionRaw(保存自己的宽高)
View measure --> onMeasure --> setMeasuredDimension -->setMeasuredDimensionRaw(保存自己的宽高)
注: 在自定义View时候,一定要重写onMeasure方法,否则View在布局中设置wrap_content
与match_parent
效果一样,自定义View要重写onMeasure
方法重新确定自己的宽高,自定义ViewGroup要重写onMeasure
确定子控件宽高,从而确定自身的宽高
详见Android自定义View的wrap_content无效?
layout
方法主要是确定控件的左上右下的值,主要逻辑在View的setFrame()
方法中实现
ViewGroup layout(来确定自己的位置,4个点的位置) -->onLayout(进行子View的布局)
View layout(来确定自己的位置,4个点的位置)
layout
方法还调用了自身的onLayout()
方法,这个方法在View中是一个protected
的空方法,供子类实现,
- 如果子类是ViewGroup可以实现该方法布局子View
- 如果子类是View,可以不实现
draw
方法主要做了以下事情
* 1. Draw the background 绘制背景
* 2. If necessary, save the canvas' layers to prepare for fading 如果需要进行图层的保存
* 3. Draw view's content 绘制自身的内容
* 4. Draw children 绘制子控件 dispatchDraw()方法
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance) 绘制装饰
综上: 在自定义View的过程中需要重写以下方法:
onMeasure()
-> onLayout()
->onDraw()
如果自定义View是个容器,那么 onLayout()
重写之后确定子View的布局,如果是个View,可以不重写这个方法,onDraw()
是个可选项,比如重写的View中包含的都是系统控件,可以选择不重写这个方法.