Android怎么描绘视图
原英文地址:http://developer.android.com/guide/topics/ui/how-android-draws.html
以下为本人原创翻译,转载请说明出处,欢迎指正!
当一个Activity获得焦点时,它将被请求去描绘它的布局(layout)。Android框架将会处理这个描绘的过程,但是Activity必须提供它的布局层次的根结点。
描绘(绘图,Drawing)由布局的根结点开始,要求测量和描绘整个布局树。描绘处理是通过遍历整个树,渲染每个与失效区域交叉的视图完成的。(Drawing is handled by walking the tree and rendering each View that intersects the invalid region.译者按:这里"invalid region"直译为失效区域,本质上是指视图的需要重新描绘的一部分,通过重绘失效区域而保持其它部分不变可以让描绘过程更加有效率。) 框架不会描绘不在失效区域的视图。框架会帮助描绘视图的背景。可以通过调用invalidate()强制一个视图描绘。
依次地,每个视图组合(View Group)将负责请求描绘它的每一个子视图(使用draw()方法),每一个视图将负责描绘自身。因为布局树是被按序遍历的,这意味着父视图将会先于它们的子视图描绘(即父视图在子视图之下),而兄弟结点视图将按其在树中出现的次序描绘。
布局的描绘是一个两阶段的过程:测量阶段和布局阶段。(Drawing the layout is a two pass process: a measure pass and a layout pass)测量阶段由measure(int , int)实现,是一个对视图树自上而下的遍历。在这个迭代中,每个视图将尺寸说明沿树向下传递。在测量阶段结束时,每个视图都将保存了其尺寸值。 第二个阶段由layout(int ,int, int, int)实现,也是一个对视图树自上而下的遍历。在这个遍历过程中,每个父视图负责使用在测量阶段计算得到的尺寸大小,定位(放置)其所有的子视图。
当一个视图的measure()方法返回时,它的getMeasuredWidth()和getMeasuredHeight()值必须被设置,并且连同其所有子视图也要一起被设置。一个视图的测量宽度和测量高度值必须遵从其父视图施加的约束。这保证了在测量阶段结束时,所有的父视图都接受了其所有子视图的尺寸。
一个父视图有可能对其子视图多次调用measure()方法。例如,如果所有子视图的非约束尺寸的总和太大了或太小了,父视图可能使用未确定尺寸(unspecified dimensions)测量每个子视图一次来发现它们想要多大的空间,然后使用实际数值再次调用measure()。(即如果子视图们不能在它们每个能占用多大空间上达成一致,那么父视图将介入调停,在第二次调用时设定规则)
测量阶段使用两个类来传递尺寸。视图用View.MeasureSpec类来告知其父视图他们想要如何被测量和定位。基本的LayoutParams类仅描述了视图想要多大的宽度和高度。 对每个维度尺寸,可以设置为以下值之一:
1)一个具体的数值
2)FILL_PARENT, 意味着视图想要和它的父视图一样大(要减去padding填充尺寸)
3)WRAP_CONTENT, 意味着视图想要刚好足够装入其内容的大小(要加上padding填充尺寸)
对不同的ViewGroup的子类,还有一些相应的LayoutParams的子类。例如, RelativeLayout有自己的LayoutParams的子类,其包含了将其子视图水平和垂直方面居中的能力。
MeasureSpecs用来将测量要求由树中的父视图向下传给子视图。一个MeasureSpec可以为以下三种模式之一:
1)UNSPECIFIED: 父视图用来确定子视图要求的尺寸。例如,一个LinearLayout可能对其高度设置为UNSPECIFIED,而宽度设置为EXACTLY 240的子视图调用measure(),来确定该子视图在给定240像素宽度下,想要多高的尺寸。
2)EXACTLY: 父视图用来强加子视图一个具体的尺寸值。 子视图必须使用该尺寸,并且保证其所有后代视图要适配该尺寸。
3)AT_MOST: 父视图用于强加子视图一个最大的尺寸值。子视图必须保证自身和其所有后代视图要适配该尺寸。
另外,可通过调用requestLayout()来初始化一个布局,该方法一般是由一个视图针对自身来调用,当其相信已不再适配当前的界限时。