最近在深入学习Android视图的绘制流程,从相对简单的FramgLayout开始。
onMeasure函数主要是用来计算子视图以及ViewGroup自身的大小,代码如下:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
//如果width或者height的mode有一个不为EXACTLY那么使能measureMatchParentChildren
final boolean measureMatchParentChildren =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
//清除mMatchParentChildren缓存,mMatchParentChildren中保存了前一次onMeasure中宽或高被设置为match_parent的view
mMatchParentChildren.clear();
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
//step1.遍历计算所有子View大小
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
//mMeasureAllChildren如果使能,那么无视可见性,强制measure所有视图,默认false
if (mMeasureAllChildren || child.getVisibility() != GONE) {
//measure子View,该函数属于ViewGroup
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
//记录子视图最大宽度
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
//记录子视图最大高度
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
//如果measureMatchParentChildren使能那么
//向mMatchParentChildren中加入所有高或宽为Match_Parent的view
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT ||
lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}
// Account for padding too
// 考虑padding
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
// Check against our minimum height and width
// 与最小宽高比较,取较大值
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
// Check against our foreground's minimum height and width
// 与前景图片比较,获取宽高相对较大的值
final Drawable drawable = getForeground();
if (drawable != null) {
maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}
//step2.设置FrameLaout自身的尺寸
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
//step3.绘制宽或高为Match_Parent的view
count = mMatchParentChildren.size();
if (count > 1) {
for (int i = 0; i < count; i++) {
final View child = mMatchParentChildren.get(i);
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
int childWidthMeasureSpec;
int childHeightMeasureSpec;
if (lp.width == LayoutParams.MATCH_PARENT) {
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() -
getPaddingLeftWithForeground() - getPaddingRightWithForeground() -
lp.leftMargin - lp.rightMargin,
MeasureSpec.EXACTLY);
} else {
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
lp.leftMargin + lp.rightMargin,
lp.width);
}
if (lp.height == LayoutParams.MATCH_PARENT) {
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() -
getPaddingTopWithForeground() - getPaddingBottomWithForeground() -
lp.topMargin - lp.bottomMargin,
MeasureSpec.EXACTLY);
} else {
childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
lp.topMargin + lp.bottomMargin,
lp.height);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
// 考虑子视图外边距计算子视图大小
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
//根据parentWidthMeasureSpec,ViewGroup剩余宽度,child的宽度重新计算child的widthMeasureSpec
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
//根据parentHeightMeasureSpec,ViewGroup剩余高度度,child的高度重新计算childHeightMeasureSpec
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
//调用measure计算大小
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
onLayout计算子视图的位置:
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// 直接调用了FrameLayout中的layoutChildren方法
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
//该方法中的参数其实都是相对于父视图的
void layoutChildren(int left, int top, int right, int bottom,
boolean forceLeftGravity) {
final int count = getChildCount();
// 获取各个方向的padding值
final int parentLeft = getPaddingLeftWithForeground();
final int parentRight = right - left - getPaddingRightWithForeground();
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
mForegroundBoundsChanged = true;
// 循环layout所有子View
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
// 不绘制可见性为GONE的View
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
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;
}
// 获取布局方向,可能的值为rtl、ltr,分别为从右至左、从左至右布局
final int layoutDirection = getLayoutDirection();
// 结合layoutDirection和View的layout_gravity计算出真正的gravity
// Gravity.getAbsoluteGravity是一个静态方法
// 源码注释:
// if horizontal direction is LTR, then START will set LEFT and END will set RIGHT.
// if horizontal direction is RTL, then START will set RIGHT and END will set LEFT.
// 水平方向如果为从左到右,那么,Gratity为START会被设置成LEFT,END会被设置成RIGHT
// 水平方向如果为从右到左,那么,Gratity为START会被设置成RIGHT,END会被设置成LEFT
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
// 获取垂直方向的Gravity
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
// 根据不同的水平比重计算View的水平坐标
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
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;
}
//根据垂直比重计算View的垂直坐标
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;
}
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
FrameLayout并没有继承onDraw方法,而是继承了draw:
// 仅仅绘制了前景图片
public void draw(Canvas canvas) {
super.draw(canvas);
if (mForeground != null) {
final Drawable foreground = mForeground;
// 根据mForegroundBoundsChanged判断是否重新设置界限
// mForegroundBoundsChanged在onLayout以及onSizeChanged方法中会被使能
if (mForegroundBoundsChanged) {
mForegroundBoundsChanged = false;
final Rect selfBounds = mSelfBounds;
final Rect overlayBounds = mOverlayBounds;
final int w = mRight-mLeft;
final int h = mBottom-mTop;
// 根据mForegroundInPadding判断前景是否被padding所束缚,重新设置bounds
if (mForegroundInPadding) {
selfBounds.set(0, 0, w, h);
} else {
selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom);
}
final int layoutDirection = getLayoutDirection();
Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(),
foreground.getIntrinsicHeight(), selfBounds, overlayBounds,
layoutDirection);
foreground.setBounds(overlayBounds);
}
foreground.draw(canvas);
}
}
以上。