Android如何绘制视图

当一个Activity接收到焦点时,它将被请求绘制其布局(layout)。Android框架将处理这个绘制过程,同时Activity必须提供其布局层级的根节点。

绘制过程从布局的根节点开始。它被请求测量并绘制布局树。绘制过程通过遍历布局树并渲染每一个穿插于invalid区域View进行。每一个视图组顺次负责请求其每一个子视图进行绘制(以draw()方法),而每一个View则负责绘制自身。因为布局树是依次遍历的,这就意味着父视图将在其子视图之前(即位于其下方)被绘制,而兄弟视图根据其出现在数中的位置按顺序被绘制。

绘制布局是一个分为两条线路的过程:测量线路(measuring pass)和布局线路(layout pass)。测量线路由measure(int ,int)完成,是视图树的一次从上至下的遍历。在递归过程中每一个View沿着树向下推送它的尺寸。在测量线路的最后,每一个View就都已储存了其测量尺寸值。第二条线路在layout(int, int, int, int)中完成,它同样也是自上而下的。在这个线路中父视图负责通过在测量线路中计算得到的尺寸来定位其所有的子视图。

Android框架不允许绘制不在invalid区域内的View,同时它将负责绘制View的背景。

可以通过调用invalidate()来强制绘制一个View。

当一个View的measure()方法返回时,必须设定它与它的子孙类的getMeasuredWidth()和getMeasuredHeight()值。一个View的测量宽度和测量高度必须满足其父类的约束。这保证了在测量线路的最后,所有的父类可以符合所有子类的测量值。一个父类View可以不止一次对其子类调用measure()。例如,父类可以以未指定的尺寸来测量每一个子类以确定他们所需的尺寸,之后以实际值对其再次调用measure()来检查所有子类的未约束的尺寸是否过大或是过小。(即,如果子类和其应该占用的尺寸不符,父类将会进行干预,将规则设置为第二条线路)。【抱歉这部分翻译质量欠佳,之后会找时间考虑如何改进(其实很多部分翻译质量都需要提高,总之现在先完成第一遍再说了。渐渐开始理解那些中文版教材令人无法满意的翻译背后的翻译者了……)】

测量线路使用两个类来交流尺度。View.MeasureSpec类被View用来告诉其父类希望被测量和定位的情况。基本的LayoutParams类用来描述View所希望的高宽尺寸。对于每一个维度,可以指定以下之一:

  • 一个确切的数字
  • FILL_PARENT 表示View希望和其父类一样大(减去间隙)
  • WRAP_CONTENT 表示View希望其大小足以包含其内容(加上间隙)。

要实例化一个布局,需要调用requestLayout()。该方法也将在一个View认为自身不可能适合当前边框时自调用。

对于ViewGroup的不同子类有相对应的LayoutParams的子类。丽日,RelativeLayout有其专属的LayoutParams子类,它可以水平及垂直定位其子视图。

MeasureSpec用于将需求沿着布局树从父至子向下推送。一个MeasureSpec可以以下三种模式存在:

  • UNSPECIFIED: 这被父类用于决定某一子视图的期望尺度。例如,一个LineraLayout可以对其子类调用measure(),将高度设置为UNSPECIFIED,宽度设置为EXACTLY 240以确定该子类在被分配了240像素宽度时所需的高度是多少。
  • EXACTLY: 这被父类用于强制设置其子类的确切尺寸。子类必须使用该尺寸,且保证它的所有子嗣也都满足这个尺寸限定。
  • AT_MOST: 这被父类用语强制设置其子类的最大尺寸。子类必须保证它及其子嗣满足该尺寸。

返回“用户界面”

本作品采用知识共享 署名-非商业性使用-禁止演绎 3.0 Unported许可协议进行许可。

《Android如何绘制视图》有一个想法

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注