How Android Draws Views
When an Activity
receives focus, it will be requested to draw its layout. The Android framework will handle the procedure for drawing, but the Activity
must provide the root node of its layout hierarchy.
Drawing begins with the root node of the layout. It is requested to measure and draw the layout tree. Drawing is handled by walking the tree and rendering each View
that intersects the invalid region. In turn, each ViewGroup
is responsible for requesting each of its children to be drawn (with the draw()
method) and each View
is responsible for drawing itself. Because the tree is traversed in-order, this means that parents will be drawn before (i.e., behind) their children, with siblings drawn in the order they appear in the tree.
The framework will not draw View
objects that are not in the invalid region, and also will take care of drawing theView
background for you.
You can force a View
to draw, by calling invalidate()
.
1、Activity获取到焦点后,才开始绘制其布局。framework 负责处理绘制的过程,但是 Activity 必须提供布局层次上的根节点。
2、绘制View是从根节点开始的,所以要求先测量和绘制 layout tree。绘制是有顺序的:ViewGroup负责发送请求,绘制他的children(调用draw( ));View 负责自己绘制自己:因为layout tree 的测量时有顺序的,也就是说,parents view 先绘制,child view 后面绘制。绘制child view 时候,是根据出现在 tree 上的顺序绘制的。
Drawing the layout is a two pass process: a measure pass and a layout pass. The measuring pass is implemented in measure(int, int)
and is a top-down traversal of the View
tree. Each View
pushes dimension specifications down the tree during the recursion. At the end of the measure pass, every View
has stored its measurements. The second pass happens in layout(int, int, int, int)
and is also top-down. During this pass each parent is responsible for positioning all of its children using the sizes computed in the measure pass.
1、绘制布局经历两个过程:测量,布局。
2、测量过程:用 measure(int,int) 实现,有顺序:从 tree 自上而下; 递归过程中,每个view 把自己的尺寸推送到tree中。测量过程的最后,每个view都会记录下自己的尺寸
3、布局过程:layout(int, int, int, int)方法,也是自上而下。在这个过程中,每个 parent view负责把所有的children view放到合适的位置,用的参数是 测量过程中计算出来的尺寸。
When a View
object's measure()
method returns, its getMeasuredWidth()
and getMeasuredHeight()
values must be set, along with those for all of that View
object's descendants. A View
object's measured width and measured height values must respect the constraints imposed by the View
object's parents. This guarantees that at the end of the measure pass, all parents accept all of their children's measurements.
A parent View
may call measure()
more than once on its children.
For example, the parent may measure each child once with unspecified dimensions to find out how big they want to be, then call measure()
on them again with actual numbers if the sum of all the children's unconstrained sizes is too big or too small (that is, if the children don't agree among themselves as to how much space they each get, the parent will intervene and set the rules on the second pass).
To initiate a layout, call requestLayout()
. This method is typically called by a View
on itself when it believes that is can no longer fit within its current bounds.
1、measure()执行完毕,那么 getMeasuredWidth()
and getMeasuredHeight() 的值就必须被设定了。view的子孙view也是一样的.
2、children view 测量宽度和高度的时候,必须注意 parent view的约束。(不能比父控件大)(需要测试一下) 这样才能保证:测量过程结束的时候,所有的parents 才能接受children view的尺寸。
3、有时候,parent在测量child 的时候可能调用多于一次的
measure(),比如:parent测量child 一次,测量尺寸没有限制大小,只是测量,child view 想要让自己多大。
如果所有children view的尺寸不满足要求,太大或者太小了,那么再次调用
measure()去测量 children view,获取children 的实际尺寸。(两者不达成一致,第二次测量的时候,parent将添加限制规则,强制固定大小)
The measure pass uses two classes to communicate dimensions. The ViewGroup.LayoutParams
class is used by View
objects to tell their parents how they want to be measured and positioned. The base ViewGroup.LayoutParams
class just describes how big the View
wants to be for both width and height. For each dimension, it can specify one of:
- an exact number
MATCH_PARENT
, which means theView
wants to be as big as its parent (minus padding)WRAP_CONTENT
, which means that theView
wants to be just big enough to enclose its content (plus padding).
ViewGroup.LayoutParams:用于子view告诉父view,想自己怎么被测量,被摆放。基本的 ViewGroup.LayoutParams 仅仅是描述view想要自己多高,多宽。可以指定:
MATCH_PARENT:和父view一样大,减去padding ?
WRAP_CONTENT:足够大小,包裹自己的内容,加上padding ?
There are subclasses of ViewGroup.LayoutParams
for different subclasses of ViewGroup
. For example,RelativeLayout
has its own subclass of ViewGroup.LayoutParams
, which includes the ability to center child View
objects horizontally and vertically.
1、针对 ViewGroup
不同的子类,有其对应的不同的 ViewGroup.LayoutParams
。力例如:RelativeLayout就有自己对应ViewGroup.LayoutParams的子类,里面包含了把子view摆放成垂直还是水平
各种布局都是ViewGroup,各种布局之间的属性是不一样的,每个布局有不同的 ViewGroup.LayoutParams
MeasureSpec
objects are used to push requirements down the tree from parent to child. A MeasureSpec
can be in one of three modes:
UNSPECIFIED
: This is used by a parent to determine the desired dimension of a childView
. For example, aLinearLayout
may callmeasure()
on its child with the height set toUNSPECIFIED
and a width ofEXACTLY
240 to find out how tall the childView
wants to be given a width of 240 pixels.EXACTLY
: This is used by the parent to impose an exact size on the child. The child must use this size, and guarantee that all of its descendants will fit within this size.
AT MOST
: This is used by the parent to impose a maximum size on the child. The child must guarantee that it and all of its descendants will fit within this size.
MeasureSpec 有三种模式:
LinearLayout设置高度的时候用 UNSPECIFIED,设置宽度的时候,用 EXACTLY 240像素
EXACTLY:指定准确尺寸:parent 强加到child view一个实际尺寸,child 必须使用,并且保证 child 的所有子孙 view 都满足条件
AT MOST:限制最大尺寸:parent 限制child 的最大尺寸。child 必须遵守,并且 child 的所有子孙都满足