一、项目中用到流布局 总结 参考于:http://blog.csdn.net/jdsjlzx/article/details/45042081?ref=myread
项目中需要用到搜索标签,需要根据每个标签的大小使布局自适应
首先想要自定义一个可包含不同大小长度的布局,就需要继承自viewGroup,重写其两个重要方法
1、重写onMeasure()方法,用于将此布局中包含的所有控件进行测量,同时测量出该流布局的宽和高
//测量子控件的总长度和高度
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 清空数据集合
childViewList.clear();
lineHeightList.clear();
// 获取子view的数量
int childCount = getChildCount();
//获取容器的宽和高
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
// 每一行的实际宽和高
int lineWidth = 0;
int lineHeight = 0;
// 实际测量出的容器的宽和高
int width = 0;
int height = 0;
// 创建存放每一行子view的集合
List<View> viewList = new ArrayList<View>();
for (int i = 0; i < childCount; i++) {
// 测量每一个子view
View childView = getChildAt(i);
// 对子view进行测量
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
// 获取到边距参数
MarginLayoutParams mlp = (MarginLayoutParams) childView.getLayoutParams();
// 计算出子view占据的宽度和高度
int childWidth = childView.getMeasuredWidth() + mlp.leftMargin + mlp.rightMargin;
int childHeight = childView.getMeasuredHeight() + mlp.topMargin + mlp.bottomMargin;
// 如果当前行的宽度加上当前这个子view的宽度大于了容器的宽度,需要换行
if (lineWidth + childWidth > parentWidth) {
// 保存这一行的数据
childViewList.add(viewList);
// 保存行高
lineHeightList.add(lineHeight);
// 保存这一行的行宽
width = Math.max(width, lineWidth);
// 重置行宽,换行后,当前子view的宽度即为下一行的其实宽度
lineWidth = childWidth;
// 记录容器的行高
height += lineHeight;
// 重置行高
lineHeight = childHeight;
// 重置view集合
viewList = new ArrayList<View>();
} else {
// 如果未换行的情况
// 添加这一行的子view到集合
viewList.add(childView);
// 叠加行宽
lineWidth += childWidth;
// 得到这一行的最大行高
lineHeight = Math.max(lineHeight, childHeight);
}
if (i == childCount - 1) {
// 将最后一行数据添加进集合
viewList.add(childView);
childViewList.add(viewList);
lineHeightList.add(lineHeight);
// 处理最后一个子view,容器总宽度取测量最大宽度和当前行宽度的最大值
width = Math.max(width, lineWidth);
// 高度叠加
height += lineHeight;
}
}
// 取出容器宽和高的测量模式
int parentWidthMode = MeasureSpec.getMode(widthMeasureSpec);
int parentHeightMode = MeasureSpec.getMode(heightMeasureSpec);
// 设置容器的测量尺寸,如果宽度的测量模式为精确测量,则将容器本身的宽和高作为尺寸
// 否则,将我们动态测量的宽和高作为容器的尺寸
setMeasuredDimension(parentWidthMode == MeasureSpec.EXACTLY ? parentWidth : width,
parentHeightMode == MeasureSpec.EXACTLY ? parentHeight : height);
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
测量出每一行的宽度和高度,并将每行的子view添加到集合中,注意在处理最后一个子view时,要根据是否是单独一行来计算出高度和宽度,并且若换行了,则需要
再添加一行view到集合中
setMeasureDimension()方法是测量最后需要调用的方法,用于将测量的值设置给控件,是onMeasure方法最后要进行的一步,若手动调用了此方法,并且把控件
的宽高值交给父类的onMeasure方法进行测量了,会导致我们手动设置的宽高值失效。
2、重写onLayout()方法对布局内的子控件进行布局
// 对子控件进行布局
@Override
protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
// 设置子view的位置
int left = 0;
int top = 0;
// 获取到容器中view的行数
int lineCount = childViewList.size();
for (int j = 0; j < lineCount; j++) {
// 取出当前行的view集合
List<View> views = childViewList.get(j);
// 获取当前行的高度
int lineHeight = lineHeightList.get(j);
for (int l = 0; l < views.size(); l++) {
// 获取当前行的每一个view
View view = views.get(l);
// 判断当前view是否显示
if (view.getVisibility() == View.GONE) {
continue;
}
// 得到当前view的上,下,左,右位置
MarginLayoutParams childMlp = (MarginLayoutParams) view.getLayoutParams();
int childLeft = left + childMlp.leftMargin;
int childRight = childLeft + view.getMeasuredWidth();
int childTop = top + childMlp.topMargin;
int childBottom = childTop + view.getMeasuredHeight();
// 对该view进行布局
view.layout(childLeft, childTop, childRight, childBottom);
// 左边距离叠加
left += view.getMeasuredWidth() + childMlp.leftMargin + childMlp.rightMargin;
}
// 下一行,重置左边距,叠加上边距
left = 0;
top += lineHeight;
}
}
计算子控件的位置,需要得出控件的left,top和控件的宽度和高度即可