各位看官们,大家好,上一回中咱们说的是Android中自定义View之Layout的例子,这一回咱们继续说该例子。闲话休提,言归正转。让我们一起Talk Android吧!
看官们,我们在一回中主要介绍了ViewGroup的layout流程,其中主要查看了layout()
方法的源代码,想看onLayout()
方法的源代码时却发现是一个抽象方法,这也是上一回中遗留下的问题,在本章回中我们将看看它的子类是如何实现这个抽象方法的,我们以它的直接子类LinearLayout为例子来分析,下面是onLayout()方法的源代码:
@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);
}
从源代码中可以看出它依据横向布局和纵向布局使用了不同的方法去实现布局功能,我们以实现纵向布局功能的layoutVertical()
方法为例进行分析,下面它的源代码:
/**
* Position the children during a layout pass if the orientation of this
* LinearLayout is set to {@link #VERTICAL}.
*
* @see #getOrientation()
* @see #setOrientation(int)
* @see #onLayout(boolean, int, int, int, int)
* @param left
* @param top
* @param right
* @param bottom
*/
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;
// Space available for child
int childSpace = width - paddingLeft - mPaddingRight;
final int count = getVirtualChildCount();
final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
switch (majorGravity) {
case Gravity.BOTTOM:
// mTotalLength contains the padding already
childTop = mPaddingTop + bottom - top - mTotalLength;
break;
// mTotalLength contains the padding already
case Gravity.CENTER_VERTICAL:
childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
break;
case Gravity.TOP:
default:
childTop = mPaddingTop;
break;
}
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
if (child == null) {
childTop += measureNullChild(i);
} else 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);
}
}
}
从上面的代码中可以看出它主要是计算出当前布局的上下左右坐标,在计算时先计算出最上端(Top)的坐标,然后是最左端(left)的坐标,而下端和右端的坐标是通过上端和左端的坐标与高度和宽度相加计算出来的(top+hight和left+width).在计算坐标时一方面要考虑布局中控件的对齐方式(switch模块中的代码),另一方面要考虑其它子控件的布局(for循环模块中的代码)。
计算完自身的坐标后通过setChildFrame()
来计算子View的布局,下面是它的源代码,从中可以看出它是调用子View的layout方法进行计算的。
private void setChildFrame(View child, int left, int top, int width, int height) {
child.layout(left, top, left + width, top + height);
}
看官们,实现横向布局功能的layoutHorizontal()
方法我们不详细分析了,它的原理和刚才介绍的layoutVertical()
方法十分类似,大家可以自己去分析。
各位看官,关于Androd中自定义View之Layout的例子咱们就介绍到这里,欲知后面还有什么例子,且听下回分解!