布局对应的方法有两个:layout(int l, int t, int r, int b)和onLayout(boolean changed, int left, int top, int right, int bottom),layout(int l, int t, int r, int b)用于布局自身,onLayout(boolean changed, int left, int top, int right, int bottom)用于布局子view。
在layout()方法的源码中我们可以看到,系统记录了上次布局的值,同时调用了setFrame()方法。
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);
在setFrame()方法中会把这一次布局的值赋值给成员变量
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
这样上一次和这一次的数据都有了,就方便系统对这个view的大小和位置进行对比。
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
位置改变的同时还会去判断大小改变:
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
位置改变,大小不一定改变,大小改变,位置一定发生改变。
只要大小或者位置发生改变,就回去调用invalidate()方法进行重绘。
那么父view怎么给子view确定这四个位置。
if (child.getVisibility() != GONE) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
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:
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;
setChildFrame(child, childLeft, childTop + getLocationOffset(child),
childWidth, childHeight);
childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
i += getChildrenSkipCount(child, i);
}
布局文件中各个标签的属性有很多种,总体来说有两种,一种是带有layout前缀的,一种是不带有layout前缀的,带有layout属性的,是用来决定一个view大小和位置的,它代表这个view和父view、相邻view之间的关系。这个属性影响的不仅仅是自身,还影响着相邻view和父view。哪些属性会以layout开头呢?例如,外边距。而这些带有layout的属性对应的类就是LayoutPararms。