您的位置:首页 > 其它

自定义控件之流式布局FlowLayout

2016-06-13 19:33 387 查看
package com.study.googleplay.view;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

import com.study.googleplay.utils.UIUtils;

public class FlowLayout extends ViewGroup {

private int usedWidth;// 当前行已使用的宽度
private int horizontalSpacing = UIUtils.dip2px(6);// 水平间距
private int verticalSpacing = UIUtils.dip2px(8);// 垂直间距
private Line line;// 当前行对象
private List<Line> lines = new ArrayList<FlowLayout.Line>();// 维护所有行的集合
private int MAX_LINE = 100;// 最大行数

public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}

public FlowLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}

public FlowLayout(Context context) {
super(context);
}

public void setHorizontalSpacing(int horizontalSpacing) {
this.horizontalSpacing = horizontalSpacing;
}

public void setVerticalSpacing(int verticalSpacing) {
this.verticalSpacing = verticalSpacing;
}

public void setMaxLines(int maxLine) {
this.MAX_LINE = maxLine;
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {

int left = l + getPaddingLeft();
int top = t + getPaddingTop();

// 遍历所有行对象,设置每行位置
for (int i = 0; i < lines.size(); i++) {
Line line = lines.get(i);
line.layout(left, top);

top += line.getMaxHeight() + verticalSpacing;// 更新top值
}

}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 获取有效宽度
int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft()
- getPaddingRight();
// 获取有效高度
int height = MeasureSpec.getSize(heightMeasureSpec)
- getPaddingBottom() - getPaddingTop();

// 获取宽度模式
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
// 获取高度模式
int heightMode = MeasureSpec.getMode(heightMeasureSpec);

// 获取子控件个数
int childCount = getChildCount();

for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);

// 如果父控件是确定模式,子控件包裹内容,否则子控件模式和父控件一致
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
(widthMode == MeasureSpec.EXACTLY) ? MeasureSpec.AT_MOST
: widthMode);
int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
(heightMode == MeasureSpec.EXACTLY) ? MeasureSpec.AT_MOST
: heightMode);

// 开始测量
childView.measure(childWidthMeasureSpec, childHeightMeasureSpec);

// 如果行对象为空,初始化一个对象
if (line == null) {
line = new Line();
}

// 获取测量后子控件宽度
int childWidth = childView.getMeasuredWidth();

usedWidth += childWidth;// 增加当前已使用宽度
if (usedWidth < width) {// 判断是否超出了边界
line.addView(childView);// 给当前行添加子控件
usedWidth += horizontalSpacing;// 增加水平间距
if (usedWidth > width) {
// 增加了水平间距,超出了边界,要换行
if (!newLine()) {
// 换行失败,结束for循环
break;
}
}
} else {// 已超出边界
// 1.当前没有任何控件,一旦添加当前控件,就超出边界(子控件很长)
if (line.getChildCount() == 0) {
line.addView(childView);// 强制添加到当前行
if (!newLine()) {
break;
}
} else {
// 2.当前有控件,一旦添加,超出边界
if (!newLine()) {// 先换行
break;
}
line.addView(childView);// 增加
usedWidth += childWidth + horizontalSpacing;// 更新已使用宽度
}
}
}

// newLine()永远保存的是上一行数据,所以要保存最后一行的数据
if (line != null && line.getChildCount() != 0 && !lines.contains(line)) {
lines.add(line);
}

// 测量
int totalWidth = MeasureSpec.getSize(widthMeasureSpec);// 控件整体宽度
int totalHeight = 0;// 控件整体高度
for (int i = 0; i < lines.size(); i++) {
totalHeight += lines.get(i).getMaxHeight();
}
totalHeight += (lines.size() - 1) * verticalSpacing;// 增加垂直间距
totalHeight += getPaddingBottom() + getPaddingTop();// 增加上下间距

// 根据最新的宽高来测量布局大小
setMeasuredDimension(totalWidth, totalHeight);
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

// 换行
public boolean newLine() {
lines.add(line);
if (lines.size() < MAX_LINE) {
// 可以继续添加
usedWidth = 0;// 已使用宽度清零
line = new Line();
return true;
}
return false;
}

// 每一行的对象封装
class Line {

private List<View> childViewList = new ArrayList<View>();// 当前行所有子控件集合
private int totalWidth;// 当前行所有控件总宽度
private int maxHeight;// 当前控件高度(以最高的控件为准)

// 添加一个子控件
public void addView(View view) {
// 总宽度增加
totalWidth += view.getMeasuredWidth();

// 最大高度赋值
maxHeight = maxHeight > view.getMeasuredHeight() ? maxHeight : view
.getMeasuredHeight();

// 添加控件
childViewList.add(view);
}

// 子控件位置设置
public void layout(int left, int top) {
int childCount = getChildCount();

// 将剩余空间分配给子控件
int width = getMeasuredWidth() - getPaddingLeft()
- getPaddingRight();// 有效宽度
int surplusWidth = width - totalWidth - (childCount - 1)
* horizontalSpacing;// 剩余宽度

if (surplusWidth >= 0) {
// 有剩余空间

// 平均每个控件拿到的大小
int space = (int) ((float) surplusWidth / childCount + 0.5f);

// 重新测量子控件
for (int i = 0; i < childCount; i++) {
View childView = childViewList.get(i);

int measuredHeight = childView.getMeasuredHeight();
int measuredWidth = childView.getMeasuredWidth();

measuredWidth += space;

int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
measuredWidth, MeasureSpec.EXACTLY);
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(
measuredHeight, MeasureSpec.EXACTLY);

// 重新测量控件
childView.measure(widthMeasureSpec, heightMeasureSpec);

// 当控件比较矮时,需要居中展示,
int topOffest = (maxHeight - measuredHeight) / 2;
if (topOffest < 0) {
topOffest = 0;
}

// 设置控件的位置
childView.layout(left, top + topOffest, left
+ measuredWidth, top + measuredHeight + topOffest);
left += measuredWidth + horizontalSpacing;// 更新left值
}
} else {
// 这个控件很长,占满整个整行
View childView = childViewList.get(0);
childView.layout(left, top, childView.getMeasuredWidth(), top
+ childView.getMeasuredHeight());
}

}

// 返回子控件个数
public int getChildCount() {
return childViewList.size();
}

public List<View> getChildViewList() {
return childViewList;
}

public int getTotalWidth() {
return totalWidth;
}

public int getMaxHeight() {
return maxHeight;
}
}
}

调用:

// 支持上下滑动
ScrollView scrollView = new ScrollView(getActivity());
FlowLayout flowLayout = new FlowLayout(getActivity());

int padding = UIUtils.dip2px(10);
flowLayout.setPadding(padding, padding, padding, padding);
flowLayout.setHorizontalSpacing(UIUtils.dip2px(6));// 水平间距
flowLayout.setVerticalSpacing(UIUtils.dip2px(8));// 垂直间距

for (int i = 0; i < data.size(); i++) {
TextView textView = new TextView(getActivity());
textView.setText(data.get(i));
textView.setTextColor(Color.WHITE);
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
textView.setPadding(padding, padding, padding, padding);
textView.setGravity(Gravity.CENTER);

Random random = new Random();
int red = 30 + random.nextInt(200);
int green = 30 + random.nextInt(200);
int blue = 30 + random.nextInt(200);

StateListDrawable selector = DrawableUtils.getSelector(
Color.rgb(red, green, blue), 0x46868, UIUtils.dip2px(6));

textView.setBackgroundDrawable(selector);

// TextView默认是不能点击的
textView.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
return;
}
});

flowLayout.addView(textView);
}
scrollView.addView(flowLayout);
运行结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: