performTraversals
和前面分析测量过程相似,整个布局的起点也是在ViewRootImpl
的performTraversals
当中:bash
private void performTraversals() {
......
mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
......
}
复制代码
能够看到,布局过程会参考前面一步测量的结果,和测量过程的measure
和onMeasure
方法很像,布局过程也有两个方法layout
和onLayout
,参考前面的分析,咱们先对这两个方法进行介绍:ide
layout
和onLayout
View
layout
方法是public
的,和measure
不一样的是,它不是final
的,也就是说,继承于View
的控件能够重写layout
方法,可是咱们通常不这么作,由于在它的layout
方法中又调用了onLayout
,因此继承于View
的控件通常是经过重写onLayout
来实现一些逻辑。public void layout(int l, int t, int r, int b) {
//....
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
}
//...
}
复制代码
onLayout
方法是一个空实现。protected void onLayout(boolean changed, int left, int top, int right, int bottom) {}
复制代码
ViewGroup
View
中的layout
,并把它设为final
,也就是说继承于ViewGroup
的控件,不能重写layout
,在layout
方法中,又会调用super.layout
,也就是View
的layout
。@Override
public final void layout(int l, int t, int r, int b) {
if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
if (mTransition != null) {
mTransition.layoutChange(this);
}
super.layout(l, t, r, b);
} else {
// record the fact that we noop'd it; request layout when transition finishes mLayoutCalledWhileSuppressed = true; } } 复制代码
onLayout
方法重写View
中的onLayout
方法,并把它声明成了abstract
,也就是说,全部继承于ViewGroup
的控件,都必须实现onLayout
方法。@Override
protected abstract void onLayout(boolean changed, int l, int t, int r, int b);
复制代码
View
的控件 例如TextView
,咱们通常不会重写layout
,而是在onLayout
中进行简单的处理。ViewGroup
的控件LinearLayout
,因为ViewGroup
的做用是为了包裹子View
,而每一个控件因为做用不一样,布局的方法天然也不一样,这也是为了安卓要求每一个继承于ViewGroup
的控件都必须实现onLayout
方法的缘由。ViewGroup
的layout
方法不能够重写,所以,当咱们经过父容器调用一个继承于ViewGroup
的控件的layout
方法时,它最终会回调到该控件的onLayout
方法。onLayout(boolean changed, int l, int t, int r, int b)
参数说明当onLayout
方法被回调时,传入了上面这四个参数,通过前面的分析,咱们知道onLayout
是经过layout
方法调用过来,而layout
方法父容器调用的,父容器在调用的时候是根据本身的坐标来计算出宽高,并把本身的位置的左上角看成是(0,0)
点,从新决定它所属子View
的坐标,所以这个矩形的四个坐标是相对于父容器的坐标值。函数
虽然layout
在某些方面和measure
有所不一样,可是它们有一点是共通的,那就是:它们都是做为整个从根节点到叶节点传递的纽带,当从父容器到子View
传递的过程当中,咱们不直接调用onLayout
,而是调用layout
。 onMeasure
在测量过程当中负责两件事:它本身的测量和它的子View
的测量,而onLayout
不一样:它并不负责本身的布局,这是由它的父容器决定的,它仅仅负责本身的下一级子View
的布局。 再回到文章最开始的点,起点是经过mView
也就是DecorView
的layout
方法触发的,而DecorView
其实是一个FrameLayout
,通过前面的分析,咱们应该直接去看FrameLayout
的onLayout
方法:oop
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
复制代码
能够看到,在它的onLayout
当中,又调用了它的子View
的layout
,那么这时候就分为两种状况,一种是该child
是继承于ViewGroup
的控件而且它有子节点,那么child.layout
方法最终又会调用到child.onLayout
,在里面,它一样会进行和FrameLayout
所相似的操做,继续调用child
的子节点的layout
;另外一种是该child
是View
或者是继承于View
的控件或者是它是继承于ViewGroup
的控件可是没有子节点,那么到该child
节点的布局遍历过程就结束了。布局
经过分析测量和布局的过程,它们基于一个思想,把传递和实现这两个逻辑分开在不一样的函数中处理,在实现当中,再去决定是否要传递。ui