安卓开发学习之FrameLayout测量流程源码阅读
程序员文章站
2024-03-25 19:47:28
...
背景
前两天我分别阅读了下相对布局和线性布局的测量流程的源代码,今天,我就安卓开发中的框架布局(FrameLayout)的测量流程源码的阅读,进行一些记录
分步骤
框架布局的onMeasure()相比相对布局和线性布局的要简单的多,只有一百零几行,步骤也只有四步
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/**
* 步骤:
* 1、初始化变量
* 2、遍历子view,获取子view中的最大尺寸
* 3、保存当前布局的尺寸
* 4、测量match_parent的子view
*/
...
}
那就一步一步来看吧
初始化变量
int count = getChildCount();
final boolean measureMatchParentChildren =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
// 当前布局宽高,有一个不是精确模式,就要测量match_parent的子view
mMatchParentChildren.clear();
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
没啥好说的
遍历子view,获取最大宽高
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
// 子view的第一次测量
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);
// 保存子view的最大尺寸
childState = combineMeasuredStates(childState, child.getMeasuredState());
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT ||
lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
// match_parent的子view要再测量一次,因为它们的尺寸也受当前view的影响
}
}
// 加入布局自身的内间距
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
// Check against our minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
// 加上前景图片的宽度
final Drawable drawable = getForeground();
if (drawable != null) {
maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}
框架布局处理了前景图片,而线性布局和相对布局都没有处理保存当前布局尺寸
// 测量当前view,也就是给measuredWidth/Height赋值
// 故而当前view的尺寸也取决于子view中的最大尺寸
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
给match_parent的子view重新测量
// 最后根据当前view的尺寸,给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();
final int childWidthMeasureSpec;
if (lp.width == LayoutParams.MATCH_PARENT) {
final int width = Math.max(0, getMeasuredWidth()
- getPaddingLeftWithForeground() - getPaddingRightWithForeground()
- lp.leftMargin - lp.rightMargin);
// 子width = 当前width - 横向内外间距 也就是子view最大的宽度
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
width, MeasureSpec.EXACTLY);
} else {
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
lp.leftMargin + lp.rightMargin,
lp.width);
// 不是match_parent,就具体情况具体分析
}
final int childHeightMeasureSpec;
if (lp.height == LayoutParams.MATCH_PARENT) {
final int height = Math.max(0, getMeasuredHeight()
- getPaddingTopWithForeground() - getPaddingBottomWithForeground()
- lp.topMargin - lp.bottomMargin);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
height, MeasureSpec.EXACTLY);
} else {
childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
lp.topMargin + lp.bottomMargin,
lp.height);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
// 故而,match_parent的子view的尺寸,取决于当前view和自己设定的间距
}
}
可以看到,框架布局对于子view的match_parent属性处理也很简单,就是给子view最大的尺寸,这一点跟另外两个布局是一样的
结语
可以看到,框架布局的onMeasure()很简单,没有考虑规则、权重、顶点的定位这些东西,所以它在开发过程中,还是很有运用价值的。
接下来,我会在安卓开发学习之FrameLayout的layout过程里,记录框架布局的onLayout()方法的阅读