[翻译] Android是怎样绘制视图的
当一个Activity获取到焦点的时候,它的布局就开始被绘制。
绘制的过程由Android framework处理。但布局层级的根节点必须由Activity提供。
视图的绘制由布局的根节点开始,通过遍历布局树和渲染每个和无效区域交叉的视图,整个布局和它的子布局(layout tree)都会被测量并绘制。反过来,ViewGroup的职责是请求它的每个子元素被绘制(通过draw()方法),而每个View的职责则是绘制它们自己本身。(意思是ViewGroup只负责排位,而view自己负责绘制工作)因为元素是 按顺序排列的,因此,父元素总是先于子元素被绘制,而父元素的同级别元素则按照它们出现的先后顺序绘制。
布局的绘制分为两个过程: 测量过程和布局过程.
测量过程在measure(int,int)方法中实现。它是按照view tree的排列从上到下进行测量的。在测量过程结束后,每个View都保存了自己的尺寸。
布局过程在layout(int, int, int, int)方法中实现,它也是从上而下进行的。在这个过程中,子元素的父节点根据子元素在上一个步骤中取得的值安排好它们的位置。
当一个View的measure()方法返回的时候,它的getMeasuredWidth()和getMeasuredHeight()的值必须被设置好。当View的父元素对这个子View的宽高值有限制的时候,这个子View必须遵守这个限制。这样做能保证在测量过程结束的时候,子View的宽高值能被父元素所接受。一个父View可能会对它的子View调用多次measure方法。举个例子,父View第一次调用measure()方法是为了获取子View请求的宽高,宽高值取得后,父View会根据这些值再次调用measure方法,进而检测这些值是否超出了限制(太大或是太小),如果是,则父View会在第二个过程中进行限制。
在测量过程中,子元素可以使用ViewGroup.LayoutParams类告知父元素它们所请求的尺寸。
ViewGroup.LayoutParams包含两个值:
MATCH_PARENT:子元素需要和父元素一样大(减去补白[padding])
WRAP_CONTENT:子元素只需要刚好能够放置它们的内容 (加上padding)
ViewGroup不同的子类有不同的LayoutParams。例如,RelativeLayout也有自己的LayoutParams类。
这使得RelativeLayout可以在水平和垂直方向上在中心放置子元素.
而父元素则可以通过MeasureSpec类限制子元素的尺寸。MeasureSpec的值可以是下面三个中的其中一个。
UNSPECIFIED:单纯用于获取子元素请求的值,不对子元素设置限制。例如:LinearLayout可以对它的子元素的长和宽分别设置为UNSPECIFIED和EXACTLY的值为240,这样可以知道当给子元素的长是240px的时候,它所需要的高度度会是多少。
EXACTLY:父元素将子元素的值限制为指定的值。子元素必须使用这个值,并且保证它的所有后代元素在这个尺寸内可用。
AT_MOST:父元素指定子元素的最大尺寸。子元素必须保证它和它的所有后代元素在这个尺寸内可用。
注意:Android framework不会绘制不在无效区域的View,但是依然会负责绘制View的背景。
我们可以通过调用invalidate()方法强制一个View重新绘制。
通过调用requestLayout()初始化布局。当一个View对象认为现在的区域已经无法正常工作的时候就会调用这个方法。
关于有效区域和无效区域:无效区域是需要被更新的区域,有效区域无须被更新。
有windows 开发经验的人都应该知道,当一个窗口从被遮挡到出现,窗口会重新被绘制,而这个被遮挡的区域就是无效区域。因为当这个区域再次出现的时候,我们必须保证它的内容是最新的,因此需要被更新,即重新绘制.
有效区域之所有被称为有效区域,是因为它目前的状态已经是最新的,不需要更新,所以不需要被重绘。