Android如何绘制视图
原文: http://developer.android.com/guide/topics/ui/how-android-draws.html
当Activity获得焦点时,其就会被要求绘制其布局,android框架会处理具体的绘制功能,但是其布局的继承关系的根节点必须由Activity提供.
绘制的时候从布局的根节点开始,其被要求计算和绘制布局树.绘制动作会遍历布局节点树,然后渲染每个节点视图.概括来说,ViewGroup负责要求其所有的子节点完成绘制(使用draw()方法),与之相应的是,每个view负责绘制自己.因为这棵树是有序的,也就是说父节点会被早绘制,树上的后续节点会被依次绘制.
绘制动作分成两个路径进程:一个是测绘路径,一个是布局路径.绘制路径的实现方法是measure(int ,int ),其从上至下遍历view树.在每次递归过程中,view将其自身的参数提交进去.在measure最后,每个view都会存储其自身的参数.第二个路径在调用layout(int ,int ,int ,int )时运行,其也是自上向下遍历view树.在这个pass中,每个父节点负责将按照其子节点按照上面通过measure获取的参数,定位在布局中.
Android的框架只绘制有效区中的视图,并同时处理视图的背景.你也可以使用invalidate()方法强制绘制.
当一个视图的measure()方法返回时,其getMeasuredWidth()和getMeasuredHeight()方法的值就已经被设定了,后续的子节点视图也同样遵守这个原则.一个view的measured宽和measured的高的值受其父节点视图约束.如此可以保证在度量结束后,所有的父节点都可以接受其所有的子节点的参数.一个父视图的measure()方法可以被子节点多次调用.例如,父视图开始会对每个子视图首先不指定尺寸调用该方法获取每个子视图的尺寸,最后再用获取到的尺寸和去判断是否超过或者小于父视图(例如子视图不指定自己需要多少的空白,那么父视图将会接管这个事).
measure pass使用两个类来传递尺寸,view使用View.MeasureSpec类告诉其父视图自己的尺寸以及位置,最基本的LayoutParams类只用来描述视图的宽和高,对每个尺寸,可以有下面三种方式指定.
- 一个具体的数字
- fill_parent,这个意思是当前view要和其父视图同样大小(减去padding的值)
- wrap_content,这个意思是当前视图只需要包围其内容即可(包含padding).
要初始化一个布局的时候,需要调用requestLayout()方法,该方法被调用的典型场景是一个view觉得其自身不再适应当前规定的范围的时候.
LayoutParams针对ViewGroup中不同的子类提供了不同的子类,例如RelativeLayout拥有其自身的LayoutParams子类,其中包含对其子视图水平和竖直居中.
MeasureSpec用来将需求放到.一个MeasureSpec可以是下面三个中的一个方法.
- UNSPECIFIEDL: 不指定大小,这个标识父视图不指定其值,而是让子视图按照自身的需要设定,比如在LinearLayout中,父视图可以使用高度为UNSPECIFIED,宽度为EXACTLY240来调用子视图的measure()方法,获取其子视图在240宽的情况下需要多少高.
- EXACTLY: 明确指定大小,子视图只能使用其父视图指定的大小.
- AT_MOST:这个用来指定其子视图的最大值,子视图必须遵循这个限制.