由于performLayout之前是performMeasure()操作,所以不熟悉测量的小伙伴看我上一篇博客Android View 测量原理
我想了想,如果直接从ViewGroup里面的方法谈起,可能和网上很多博客一样了,但是如果只是向framework开发者分析哪些,又分析不到应用层,所以我觉得应该从performLayout()这个方法开始分析测量,因为如果在向framework层深入,那就会接触到WindowManagerService,这个过程需要掌握Binder知识,但是Binder知识很多人一时半会掌握不了,尤其是对于application开发者,不关注这些,所以从performLayout()说起。
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
...
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
try {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
...
}finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
这里的host是decorView,decorView对应的布局是一个FrameLayout,所以我们进入FrameLayout的layout方法
//传递进来的是左上右下的值
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;
//判断是不是左上右下这些值有所改变,如果改变的话为true,并且在setFrame中给mLeft...mRight赋值
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
//true进入调用到onLayout方法
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
......
}
.....
}
继续进入onLyout方法中,我们会发现是空方法,所以我们此时想到了ViewGroup
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
继续看ViewGroup的onLayout方法,可想而知每个子类都有自己的实现,我们用LinearLayout举例
@Override
protected abstract void onLayout(boolean changed,
int l, int t, int r, int b);
LinearLayout实现方法是:
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (mOrientation == VERTICAL) {
layoutVertical(l, t, r, b);
} else {
layoutHorizontal(l, t, r, b);
}
}
针对于垂直方向和水平方向不同
void layoutVertical(int left, int top, int right, int bottom) {
final int paddingLeft = mPaddingLeft;
int childTop;
int childLeft;
// Where right end of child should go
final int width = right - left;
int childRight = width - mPaddingRight;
// 水平可用宽度
int childSpace = width - paddingLeft - mPaddingRight;
final int count = getVirtualChildCount();//调用getChildCount()
final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
//gravity属性配置的值
switch (majorGravity) {
//当配置bottom时候
case Gravity.BOTTOM:
// 看出来是已父容器总内容宽度为基准的最下面
childTop = mPaddingTop + bottom - top - mTotalLength;
break;
// 同理配置的center
case Gravity.CENTER_VERTICAL:
childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
break;
// 同理配置的top
case Gravity.TOP:
default:
childTop = mPaddingTop;
break;
}
for (int i = 0; i < count; i++) {
//得到每一个孩子View
final View child = getVirtualChildAt(i);
if (child == null) {
childTop += measureNullChild(i);
} else if (child.getVisibility() != GONE) {
final int childWidth = child.getMeasuredWidth();//view的宽
final int childHeight = child.getMeasuredHeight();//view的高
final LinearLayout.LayoutParams lp =
(LinearLayout.LayoutParams) child.getLayoutParams();
int gravity = lp.gravity;
if (gravity < 0) {
gravity = minorGravity;
}
final int layoutDirection = getLayoutDirection();//得到方向
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
//水平方向的话view左侧的距离
childLeft = paddingLeft + ((childSpace - childWidth) / 2)
+ lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
childLeft = childRight - childWidth - lp.rightMargin;
break;
case Gravity.LEFT:
default:
childLeft = paddingLeft + lp.leftMargin;
break;
}
//有没有分割线
if (hasDividerBeforeChildAt(i)) {
childTop += mDividerHeight;
}
childTop += lp.topMargin;
//设置子view的坐标
setChildFrame(child, childLeft, childTop + getLocationOffset(child),
childWidth, childHeight);
//加上子view的坐标继续向下排列
childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
//下一个view
i += getChildrenSkipCount(child, i);
}
}
}
通过上面标记的注释我们知道了对每一个子view进行排列。
同时注意几个方法
setFrame
当size和position变化时,返回true。如果发生了变化,会在setFrame方法内部调用invalidate。onLayout
View中onLayout什么都没有做,在ViewGroup中,根据各自实际规则(Linear、Relative 等)对内部Views进行布局安排。getMeasuredWidth与getWidth
可以调用的时机不同:getMeasuredWidth在measure后即可调用,getWidth要在layout后才可以调用。(在发生时机之前调用的话均返回0)
含义不同:getMeasuredWidth是View计算出自己的实际大小,getWidth是在布局后的大小。最简单的,在ScrollLayout中,getHeight返回屏幕内的高度,getMeasuredHeight返回屏幕内+屏幕外的总高度。