控件的工作流程主要是指measure、layout和draw三大流程,即控件的测量、布局和绘制。其中measure确定控件的测量宽/高,layout确定控件的最终宽/高和四个顶点的位置,draw将View绘制到屏幕上。
View的测量
Android系统在绘制View前,要对View进行测量,告诉系统View的大小,这个过程是在View的onMeasure()方法中进行的。
具体测量View是通过MeasureSpec类,测量模式有一下三种:
EXACTLY
当控件的layout_width和layout_height属性为具体数值,或者被指定为match_parent时,使用的就是该模式;AT_MOST
当控件的layout_width和layout_height属性被指定为wrap_content时使用,这时控件的大小会随着子控件的大小变化,同时控件的尺寸不允许超过父控件的最大尺寸;UNSPECIFIED
在这个模式下,View大小可以随意指定,通常在绘制自定义View的时候使用;
MeasureSpec类除了指定View的测量模式外,还指定了对应测量模式下View的尺寸。
MeasureSpec是一个32位的int值,其中高2位为测量的模式,低30位为测量的大小,为了提高运算效率,具体获取模式和大小的时候使用位运算。
View的默认onMeasure()方法只支持EXACTLY模式,如果想让View支持wrap_content属性,需要重写onMeasure()方法,指定AT_MOST模式下View的大小。
View的绘制
View测量完毕后,需要重写onDraw()方法,在其Canvas对象上使用Paint对象绘制所需的图形。可以把Canvas对象看作成一块画板,Paint对象看作画笔,然后在onDraw()方法中进行绘制操作。
调用Canvas的drawXXX()方法绘制图形、文字、图片等,对于复杂的控件,可以把其拆分为一个个小的图形单元分别绘制。
ViewGroup的测量
ViewGroup在测量时,会遍历所有的子View,调用子View的measure()方法获取每个子View的测量结果,这时子View会调用onMeasure()方法进行测量。
类似于View,如果需要支持wrap_content属性,也需要重写onMeasure()方法。
ViewGroup的布局
当子View测量完毕后,还需要把子View放到合适的位置,这个过程就是ViewGroup的布局过程。ViewGroup在执行布局过程时,同样会遍历调用子View的layout()方法,然后子View调用onLayout()方法进行具体的布局过程。在布局过程中,layout()方法确定View本身的位置,而onLayout()方法确定所有子View的位置。
在自定义ViewGroup时,通常需要重写onLayout()方法来控制其子View的显示位置。
ViewGroup的绘制
ViewGroup通常不需要绘制,因为其本身没有需要绘制的内容,如果不指定背景颜色,ViewGroup的onDraw()方法都不会被调用。但是,ViewGroup会使用dispatchDraw()方法来绘制其子View,这个过程也会遍历所有的子View,并调用子View的draw()方法完成绘制工作。