Android-自定义控件之FlowLayout(二)

(首先声明,这篇文章是博主在 mooc 上学习了 hyman 的视频打造Android流式布局和热门标签后总结的小知识)

onMeasure实现过程

这篇文章总结一下 onMeasure 函数该如何完成测量过程。

再次看看官网对 onMeasure 函数的说明

1
2
3
4
5
6
/**
* Ask all children to measure themselves and compute the measurement of this
* layout based on the children.
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)

前面讲过,我们通过该函数两个参数来确定 ViewGroup 的宽高及其测量模式。但同时,这个函数需要让子 View 去测量它们自己的宽高,这样,我们才能在 ViewGroup 中得到子 View 的宽高。让子 View 去测量自己的方法是调用 ViewGroup 提供的 measureChild 方法。调用该方法后,可以通过子 View 的 getMeasuredWidthgetMeasuredHeight 方法分别获得子 View 的宽高。之后通过我们自己的策略确定 ViewGroup 的宽高。前面讲过,如果是「EXACTLY」模式,那么宽高的值直接就是 onMeasure 传进来的参数值,如果是「AT_MOST」模式,则需要根据子 View 的宽高自行测量,最后通过 setMeasureDimension 方法将宽高作为参数传给 ViewGroup。

下面的代码是 FlowLayout 的 onMeasure 函数的实现方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 如果布局文件的宽高使用match_parent或fill_parent,mode对应的是EXACTLY模式
// 如果布局文件的宽高使用wrap_content,mode对应的是AT_MOST模式
// EXACTLY模式中ViewGroup的宽高已经确定,AT_MOST模式中需要自己设定
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);

// 以下计算当模式设定为AT_MOST时宽高的值

// 计算出的ViewGroup的宽和高
int width = 0;
int height = 0;
// 每一行的宽高
int lineWidth = 0;
int lineHeight = 0;
int cCount = getChildCount();
for (int i = 0; i < cCount; i++) {
View child = getChildAt(i);
// 测量子View的宽和高
measureChild(child, widthMeasureSpec, heightMeasureSpec);
// 得到子View的LayoutParams,子View的LayoutParams是由父布局的LayoutParams决定的
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
if (lineWidth + childWidth > sizeWidth - getPaddingLeft() - getPaddingRight()) {
width = Math.max(width, lineWidth);
lineWidth = childWidth;
height += lineHeight;
lineHeight = childHeight;
} else {
lineWidth += childWidth;
lineHeight = Math.max(lineHeight, childHeight);
}

if (i == cCount - 1) {
width = Math.max(width, lineWidth);
height += lineHeight;
}
}

Log.i(TAG, "sizeWidth====>" + sizeWidth + " sizeHeight===>" + sizeHeight);
Log.i(TAG, "width====>" + width + " height===>" + height);

setMeasuredDimension(
modeWidth == MeasureSpec.AT_MOST ? width + getPaddingLeft() + getPaddingRight() : sizeWidth,
modeHeight == MeasureSpec.AT_MOST ? height + getPaddingTop() + getPaddingBottom() : sizeHeight
);

}