layout的调用过程
view的三大流程是从performTravsals()开始的,我们从这里看起
ViewRootImpl#performTraversals()
performTraversals()调用performLayout()
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
if (didLayout) {
//lp是窗口的LayoutParams 和 窗口的宽高
performLayout(lp, mWidth, mHeight);
......
}
ViewRootImpl#performLayout()
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
//host就是decroview
final View host = mView;
if (host == null) {
return;
}
try {
//传入decroview的四个顶点
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
......
}
}
ViewGroup#layout()
当我们定位到layout方法时,它进入的是view的layout方法,因为ViewGroup是继承自View的,我们先看它的layout方法,发现layout是用final修饰的,也就是ViewGroup的layout方法是不能被继承的
public final void layout(int l, int t, int r, int b) {
if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
if (mTransition != null) {
mTransition.layoutChange(this);
}
//调用view的layout
super.layout(l, t, r, b);
} else {
// record the fact that we noop'd it; request layout when transition finishes
mLayoutCalledWhileSuppressed = true;
}
}
View#layout()
调用layout方法,确定view本身的位置
public void layout(int l, int t, int r, int b) {
......
//通过setFrame方法来确定View的四个顶点的位置
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
//调用onLayout方法 父容器确定子元素的位置
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
......
}
......
}
setFrame()
通过确定view的宽高是否发生改变,来修改mLeft mRight mTop mBottom这四个确定view位置的值
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
// Remember our drawn bit
int drawn = mPrivateFlags & PFLAG_DRAWN;
int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
// Invalidate our old position
invalidate(sizeChanged);
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
mPrivateFlags |= PFLAG_HAS_BOUNDS;
if (sizeChanged) {
sizeChange(newWidth, newHeight, oldWidth, oldHeight);
}
......
}
}
View#onLayout()
View的onLayout方法是空实现,需要我们人为去实现。ViewGroup的onLayout是一个抽象方法,在我们自定义view的时候需要去重写。
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
我们进入FrameLayout的onLayout方法中去看看
FrameLayout#onLayout()
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
//把父容器的参数传进
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
FrameLayout#layoutChildren()
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
//获取子view的个数
final int count = getChildCount();
//下面四个值会影响到子view的布局
//parentLeft由父容器的padding和foreground决定
final int parentLeft = getPaddingLeftWithForeground();
//parentRight由父容器的width、padding、foreground决定
final int parentRight = right - left - getPaddingRightWithForeground();
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
//遍历子元素
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
//获取子view测量的宽高
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;
//当子view设置了水平方向上的layout_gravity
//childLeft是子view的左上角的值
switch (absoluteGravity &
Gravity.HORIZONTAL_GRAVITY_MASK) {
//当子view居中显示,childLeft = (父容器的宽度 - 子view的宽度)/2 + 父view左上角的距离 + 若有margin,左margin - 右margin
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
//水平居右,childLeft = parentRight - 子view宽度-子view的rightMargin
case Gravity.RIGHT:
if (!forceLeftGravity) {
childLeft = parentRight - width - lp.rightMargin;
break;
}
//水平居左 childLeft = parentLeft+子view的margin
case Gravity.LEFT:
default:
childLeft = parentLeft + lp.leftMargin;
}
//当子view设置了水平方向上的layout_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;
}
//调用layout确定子view本身的位置
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
总结
view的layout过程
- 从ViewRootImpl的performTraversals()方法开始调用perforLayout()方法
- performLayout方法调用layout方法,通过确定mLeft、mTop、mRight、mBottom四个值确定view的位置
- 再调用onLayout去确定子view的位置
网友评论