这三种布局都是常用的布局方式,继承自ViewGroup,它们的区别在于measure和layout过程
FrameLayout: measure过程中因为每个View都在一层,所以如果FrameLayout的宽,高没有设置固定大小或者Match_Parent,
则它的宽,高就是每个View中的最大宽,高,
Measure一次
layout过程也很简单,判断每个View的Gravity,Gravity包含两个方向: 水平方向和垂直方向的Gravity,根据Gravity来计算子View的位置。
LinearLayout: measure过程中,如果LinearLayout的宽,高没有设置固定大小或者Match_Parent,则它的宽,高就是每个View宽,高 +左右,上下margin + LineayLayout本身的上下左右Padding。
1 在第一次循环中,如果当前LinearLayout为固定大小,且子View的layout_width = 0; layout_weight > 0,那么这些view先不被测量
2 如果存在LinearLayout为固定大小,且子View的layout_width = 0; layout_weight > 0,需要进行第二次循环,测量这写view的大小
即 LinearLayout最多measure 2次,
layout过程也很简单,根据LinearLayout的方向,判断是measureHorizontal还是mearsureVertical,我们以LayoutHorizontal为例
首先根据LinearLayout本身设置的Gravity,来确定第一个view的left坐标
然后进行循环,从左向右,来判断所有view的left,top,right,bottom
RelativeLayout:
因为其内部所有的view都是相对RelativeLayout或者相对其他View的布局
在measure过程中,分为2次,一次是水平关系上的measure,一次是垂直方向的measure
首先将相对关系转换成坐标,然后开始测量view,再然后,通过相对关系,以及view大小,确定view的left,top,right,bottom坐标
layout过程: 由于在measure过程中,所有view的坐标已经
介绍一下在上面三种Layout中的View水平居中或垂直居中的设置
1 FrameLayout
1) 如果设置了水平居中:
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
//水平居中: 计算时,需要考虑到view的左, 右 margin
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;
}
2) 如果设置了垂直居中
switch (verticalGravity) {
case Gravity.TOP:
childTop = parentTop + lp.topMargin;
break;
//垂直居中, 计算时需要考虑view的上,下margin
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;
}
LinearLayout
如果LinearLayout为水平方向
1 如果LinearLayout设置了center_horizontal,则根据这个Gravity 计算第一个view的left坐标,然后计算每个子view的Top坐标
switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) {
case Gravity.RIGHT:
// mTotalLength contains the padding already
childLeft = mPaddingLeft + right - left - mTotalLength;
break;
case Gravity.CENTER_HORIZONTAL:
// mTotalLength contains the padding already
childLeft = mPaddingLeft + (right - left - mTotalLength) / 2;
break;
case Gravity.LEFT:
default:
childLeft = mPaddingLeft;
break;
}
然后判断每个view是否设置了center_vertical,如果设置了,
switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
case Gravity.TOP:
childTop = paddingTop + lp.topMargin;
if (childBaseline != -1) {
childTop += maxAscent[INDEX_TOP] - childBaseline;
}
break;
//垂直居中,
case Gravity.CENTER_VERTICAL:
// Removed support for baseline alignment when layout_gravity or
// gravity == center_vertical. See bug #1038483.
// Keep the code around if we need to re-enable this feature
// if (childBaseline != -1) {
// // Align baselines vertically only if the child is smaller than us
// if (childSpace - childHeight > 0) {
// childTop = paddingTop + (childSpace / 2) - childBaseline;
// } else {
// childTop = paddingTop + (childSpace - childHeight) / 2;
// }
// } else {
// childSpace为linearLayout高度减去linearLayout上 下 Padding
childTop = paddingTop + ((childSpace - childHeight) / 2)
+ lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = childBottom - childHeight - lp.bottomMargin;
if (childBaseline != -1) {
int descent = child.getMeasuredHeight() - childBaseline;
childTop -= (maxDescent[INDEX_BOTTOM] - descent);
}
break;
default:
childTop = paddingTop;
break;
}
如果LinearLayout为垂直方向,和上面类似, 先判断垂直方向的第一个view的Top坐标,然后判断每个子view的Gravity,计算每一子view的left坐标,
LinearLayout 和 FrameLayout中,设置子view水平或垂直居中,都考虑了 Layout本身的padding以及子view的margin。
LinearLayout oritation 为horizontal是,gravity为center_vertical的情况下,子view的坐标计算:
mTop = mPaddingTop(LinearLayout的paddingTop) + (childSpace(LinearLayout的高度- (paddingTop+paddingBottom)) - childHeight) /2 + marginTop(childView) - marginBottom(ChildView)
RelativeLayout
如果view设置了layout_centerInParent 为 true,则计算其水平居中和垂直居中
//水平居中, left坐标为(RelativeLayout width - childWidth) /2
private static void centerHorizontal(View child, LayoutParams params, int myWidth) {
int childWidth = child.getMeasuredWidth();
int left = (myWidth - childWidth) / 2;
params.mLeft = left;
params.mRight = left + childWidth;
}
//垂直居中, left坐标为(RelativeLayout height - childHeight) /2
private static void centerVertical(View child, LayoutParams params, int myHeight) {
int childHeight = child.getMeasuredHeight();
int top = (myHeight - childHeight) / 2;
params.mTop = top;
params.mBottom = top + childHeight;
}
RelativeLayout的
垂直居中,在mearsure阶段,Relative以及将自身 padding 以及 子 view的margin,综合计算,得出 子view的高度
子view的mTop = (RelativeLayout的 height - 子view高度(RelativeLayout - Releative的paddingtop,paddingBottom, view的marginTop,marginBotton) ) / 2
所以RelativeLayout一定可以居中对齐,而设置在linearLayout gravity=center_vertical 并不保证居中对齐