当某个 Activity
获得焦点时,系统会要求它绘制自己的布局。Android 框架会处理绘制流程,但该 Activity
必须提供其布局层次结构的根节点。
绘制从布局的根节点开始,需要测量并绘制布局树。系统通过遍历布局树并渲染与无效区域相交的每个 View
来处理绘制。反过来,每个 ViewGroup
负责请求绘制其每个子级(使用 draw()
方法),而每个 View
负责绘制其本身。由于布局树已经过系统预先遍历,这意味着父级将在它们的子级之前(即后面)进行绘制,而其同级会按照它们在布局树中出现的顺序进行绘制。
注意:该框架不会绘制有效区域之外的 View
对象,并且也不会负责为您绘制 View
背景。
您可以通过调用 invalidate()
来强制绘制 View
。
绘制布局包含两个遍历流程:一个测量遍历和一个布局遍历。
测量遍历
测量遍历在 measure(int, int)
中实现,是 View
树的自上而下遍历。在递归过程中,每个 View
都会将维度规范下推到布局树。在测量遍历结束时,每个 View
均存储了其测量值。第二次遍历发生在 layout(int, int, int, int)
中,也是自上而下遍历。在此次遍历中,每个父级负责使用测量遍历中计算的尺寸来定位其所有的子级。
当返回 View
对象的 measure()
方法时,必须设置其 getMeasuredWidth()
和 getMeasuredHeight()
值,以及该 View
对象的所有子级的值。View
对象的测量宽度值和测量高度值必须遵守 View
对象的父级所施加的限制。这就保证了在测量遍历结束时,所有父级都会接受其子级的所有测量值。父级 View
可以对其子级多次调用 measure()
。例如,父级可以使用未指定的维度测量每个子级一次,以确定它们希望的大小;然后,如果所有子级不受限制的尺寸的总和过大或过小,则再次使用实际的数字对它们调用 measure()
(即,如果子级未就各自获得多少空间达成一致,则父级将会介入并针对第二次遍历设置规则)。
测量遍历使用两个类来传达维度。View
对象使用 ViewGroup.LayoutParams
类来告知父级它们想要如何测量和定位。基本的 ViewGroup.LayoutParams
类仅描述了 View
希望的宽度和高度。针对每个维度,它可以指定以下某一项:
- 一个确切的数字
MATCH_PARENT
,该参数意味着View
想要和它的父级一样大(负填充)WRAP_CONTENT
,该参数意味着View
想要足够大,以包含其内容(正填充)。
有适用于 ViewGroup
的不同子类的 ViewGroup.LayoutParams
子类。例如,RelativeLayout
有自己的 ViewGroup.LayoutParams
子类,其中包括使子级 View
对象水平和垂直居中的功能。
MeasureSpec
对象用于在树中将要求从父级下推到子级。MeasureSpec
可以为以下三种模式之一:
UNSPECIFIED
:父级使用该模式来确定子级View
所需的维度。例如,LinearLayout
可能会对其高度设置为UNSPECIFIED
且宽度设置为EXACTLY
240 的子级调用measure()
,从而确定宽度为 240 像素的子级View
所需的高度。EXACTLY
:父级使用该模式来强制子级使用某个确切尺寸。子级必须使用尺寸,并保证其所有的子项都能放入此尺寸。AT MOST
:父级使用该模式来强制规定子级的最大尺寸。子级必须保证它及其所有的子项都能放入此尺寸。
布局遍历
如需启动布局,请调用 requestLayout()
。当 View
认为自己无法再放入当前范围时,通常会调用此方法。