自定义控件之流式布局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);运行结果:
相关文章推荐
- 【JAVA学习】“-Xmx1024m -Xms1024m -Xmn512m -Xss256k”——Java运行参数
- 【JZOJ 4496】【GDSOI 2016】第一题 互补约数 (两种解法)
- Xutils3Android功能最广的开源框架
- Ubuntu 下环境搭建系列 —— 安装 Oracle JDK
- “too many open files" ----增大打开的文件数
- 建立面向对象模型
- YUV fullrange
- Android初级教程人品计算器
- Android初级教程人品计算器
- Hibernate分页
- 员工管理系统
- Effective C++--条款06:如何禁止编译器自动生成函数
- C++逗号运算符的作用
- [Android] 获取网络图片并显示
- 【GDOI2014模拟】服务器
- 使用PHP实现验证码
- 指针和链表
- iOS 应用将适应新的的IPv6-only网络 -解决方案
- 交换机、路由器、网关的概念,并知道各自的用途
- linux中常用文件操作调用系统