layout - 布局
确定View的最终宽高以及四个顶点的位置!
接着上一篇 View的绘制流程分析之二 – measure 往下分析layout过程!
在ViewRootImpl 中的performTraversals() 函数内部,执行performMeasure() 完毕之后,
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
// ...
layoutRequested = true;
- 1
- 2
- 3
- 4
- 5
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
performLayout(lp, mWidth, mHeight); // 开始布局过程!
// ...
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
// ...
final View host = mView;
try {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
// ...
}
} finally {
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
host就是那个DecorView对象!
然后又走到了View中的 layout()方法!
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);
// ...
}
// ...
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
在layout() 内部调用了onLayout() 函数!
onLayout() 在View.java中是一个空实现!
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
- 1
- 2
来看ViewGroup中
@Override
protected abstract void onLayout(boolean changed,
int l, int t, int r, int b);
- 1
- 2
- 3
也是空实现,变成了一个抽象方法!!!
其实,不同的ViewGroup对象,有不同的展现形式,所以需要在不同的ViewGroup对象实现各自的onLayout() 函数!
还是那FrameLayout来举例!
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
- 1
- 2
- 3
- 4
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
// 1. 获取子view的个数
final int count = getChildCount();
// 2. FrameLayout的左侧padding值
final int parentLeft = getPaddingLeftWithForeground();
// 3. FrameLayout的右侧padding值
final int parentRight = right - left - getPaddingRightWithForeground();
// 4. FrameLayout的顶部padding值
final int parentTop = getPaddingTopWithForeground();
// 5. FrameLayout的底侧padding值
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
// 6. 遍历子view
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
// 7. 只操作没有GONE的view
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;
// 8. 判断 FrameLayout横向的gravity属性
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;
}
// 9. 判断 FrameLayout竖向的gravity属性
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;
}
// 开始子view测量过程
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
注释已经写得很详细了,就不在过多介绍!
child.layout() 是针对一个个子view进行测量!
如果 child是一个viewGroup,则会调用ViewGroup中的layout()函数,然后调用具体ViewGroup子类的onLayout()函数!
如果child是一个View,则会调用View中的layout()函数,然后调用具体View子类的onLayout()函数!
父容器在layout()方法中完成了自己的布局之后,就通过onLayout()方法去调用子view的layout()方法,然后子view通过自己的layout()函数来完成自己的布局,这样一层层往下传递就完成了这个View流程树的layout过程!