源码版本Android 6.0
请参阅:http://androidxref.com/6.0.1_r10
本文目的是分析从Activity启动到走完绘制流程并显示在界面上的过程,在源码展示阶段为了使跟踪代码逻辑更清晰会省略掉一部分非主干的代码,具体详细代码请翻阅源码。
上一篇我们分析到UI绘制过程中的measure流程,接下来我们来分析一下layout过程,layout过程相对于measure过程简单很多,还是从ViewRootImpl#performTraversals()
方法调用performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight)
开始,我们来分析一下从最顶层DecorView开始布局的流程:
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight) {
// 标识是否layout完成
mLayoutRequested = false;
mScrollMayChange = true;
// 标识当前是否正在执行该View的layout流程
mInLayout = true;
final View host = mView;
...
try {
// 执行DecorView的layout流程
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mInLayout = false;
...
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
mInLayout = false;
}
由于DecorView extends FrameLayout
,所以会调用到FrameLayout没有实现layout(int l, int t, int r, int b)
所以最终调用ViewGroup#layout(int l, int t, int r, int b)
#class in ViewGroup
public final void layout(int l, int t, int r, int b) {
...
super.layout(l, t, r, b);
...
}
最终调用View#layout(l,t,r,b)
#class in View
public void layout(int l, int t, int r, int b) {
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
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);
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
...
}
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}
而View#onLayout(changed,l,t,r,b)
是个空实现,所以会走到FrameLayout中:
#class in FrameLayout
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();
final int parentLeft = getPaddingLeftWithForeground();
final int parentRight = right - left - getPaddingRightWithForeground();
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
// 上面几行代码获取到父控件的上下左右,接下来根据每个子View的布局参数对每一个子View进行摆放
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft;
int childTop;
int gravity = lp.gravity;
if (gravity == -1) {
gravity = DEFAULT_CHILD_GRAVITY;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
if (!forceLeftGravity) {
childLeft = parentRight - width - lp.rightMargin;
break;
}
case Gravity.LEFT:
default:
childLeft = parentLeft + lp.leftMargin;
}
switch (verticalGravity) {
case Gravity.TOP:
childTop = parentTop + lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = parentTop + (parentBottom - parentTop - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentBottom - height - lp.bottomMargin;
break;
default:
childTop = parentTop + lp.topMargin;
}
// 继续摆放子的子
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
整个layout过程如下:
- 执行
ViewRootImpl#performLayout()
方法,内部会调用host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
,layout的调用过程是:
DecorView -> FrameLayout -> ViewGroup ->View
最终是回调到View#layout() - View#layout()中判断如果布局有改变则会回调
onLayout(changed, l, t, r, b);
,此时代码走向是:
View -> FrameLayout
最终回调到FrameLayout#onLayout() -> FrameLayout#layoutChildren()
先对自己布局然后对所有的子View进行布局 - 对子View布局的过程是又回调
child.layout(childLeft, childTop, childLeft + width, childTop + height);
到这里布局就结束了。
注意:
当我们自定义View时,如果自定义的View是一个非容器类视图,那么直接继承自View,在无特定需求的情况下onLayout(boolean changed, int left, int top, int right, int bottom)
可以不实现。但是当你定义的是一个容器类View,那需要继承自ViewGroup,此时protected abstract void onLayout(boolean changed, int l, int t, int r, int b);
是个抽象方法必须实现,然后在内部对子视图进行layout。