1 绘制流程
View的绘制流程从ViewRootImpl的requestLayout()开始
2 measure流程
- ViewGroup:每个ViewGroup必须复写onMeasure,并且在onMeasure中measureChild,并在measureChild结束之后,调用setMeasuredDimension设置自身的宽高。
final void measure(int widthMeasureSpec, int heightMeasureSpec){
onMeasure(widthMeasureSpec,heightMeasureSpec);
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
//measureChildren
省略测量子布局的代码
setMeasuredDimension(参数);
}
- View:每个View也必须复写onMeasure,并且在onMeasure中设置自身宽高。
final void measure(int widthMeasureSpec, int heightMeasureSpec){
//省略其它代码
onMeasure(widthMeasureSpec,heightMeasureSpec);
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
影响View宽高的因素
即ViewGroup先根据每个child的MarginLayoutParams(继承自ViewGroup.LayoutParams,包括android:layout_width、android:layout_height以及各方向的margin),结合自身的MeasureSpec(32位的int类型,包含测量模式与测量大小,其中测量模式分为无约束UNSPECIFIED、精确值EXACTLY以及最大值AT _MOST三种)和padding,得到需要传给各个child的约束条件MeasureSpec,然后child根据MeasureSpec,minWidth属性以及background的最小宽高了,确定出child自身的宽高。
当某个View的measure()方法返回时,它以及它的所有子节点的getMeasuredWidth()和getMeasuredHeight()方法的值就已经设置好了。
3 layout流程
public void layout(int l, int t, int r, int b){
//省略其它代码
setFrame(l, t, r, b);
onLayout(changed, l, t, r, b);
}
protected boolean setFrame(int left, int top, int right, int bottom)
layout方法确定view自身的位置,通过setFrame()设置四个顶点的坐标。然后调用onLayout()确定所有子元素的位置。
所以在自定义view中,对于ViewGroup,需要重写onLayout()方法,并调用child.layout()对子元素进行布局。如果是View,由于没有子View,无需重写该方法。
注意:view可以重写layout和onLayout,而ViewGroup的只能重写onLayout,其layout是final类型。
4 draw流程
可以看一下,draw事件是如何从上往下传递,逐个绘制的:
5 多次measure、layout和draw
measure、layout和draw的流程有可能反反复复多次,见下图。
- ViewGroup导致的重绘
一个父View可能对其子View调用多次measure()方法。举个例子:父节点可能首先会通过一次没有明确尺寸约束(unspecified dimensions)的测量过程来获取每个子View想获得的视图大小。如果最后得到的数值过大或者过小,那么父节点会再次对其子View调用measure()方法,并使用实际的计算结果作为输入参数(即如果子View不同意首次测量结果,父View会进行第二次带约束条件的测量)。
我们知道,顶层View为继承自FrameLayout的DecorView,看看FrameLayout的measure方法:
// FrameLayout的onMeasure函数,DecorView的onMeasure会调用这个函数。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
.....
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
......
}
}
........
count = mMatchParentChildren.size();
if (count > 1) {
for (int i = 0; i < count; i++) {
........
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
当然,不可能每次都执行两次measure,View的Measure函数中有相关机制,只有在FLAG_FORCE_LAYOUT标志位为1或者widthMeasureSpec、heightMeasureSpec和上次的不一样时才会重新measure。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
......
// 当FLAG_FORCE_LAYOUT位为1时,就是当前视图请求一次布局操作
//或者当前widthSpec和heightSpec不等于上次调用时传入的参数的时候
//才进行从新绘制。
if (forceLayout || !matchingSize &&
(widthMeasureSpec != mOldWidthMeasureSpec ||
heightMeasureSpec != mOldHeightMeasureSpec)) {
......
onMeasure(widthMeasureSpec, heightMeasureSpec);
......
}
......
}
- ViewRootImpl导致的重绘
Activity加载的时候,newSurface是true。
private void performTraversals() {
......
boolean newSurface = false;
//TODO:决定是否让newSurface为true,导致后边是否让performDraw无法被调用,而是重新scheduleTraversals
if (!hadSurface) {
if (mSurface.isValid()) {
// If we are creating a new surface, then we need to
// completely redraw it. Also, when we get to the
// point of drawing it we will hold off and schedule
// a new traversal instead. This is so we can tell the
// window manager about all of the windows being displayed
// before actually drawing them, so it can display then
// all at once.
newSurface = true;
.....
}
}
......
if (!cancelDraw && !newSurface) {
if (!skipDraw || mReportNextDraw) {
......
performDraw();
}
} else { //newSurface为true,会重新scheduleTraversals
if (viewVisibility == View.VISIBLE) {
// Try again
scheduleTraversals();
} else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).endChangingAnimations();
}
mPendingTransitions.clear();
}
}
......
}