自定义控件之流式布局FlowLayout

来源:互联网 发布:电脑mac地址怎么看 编辑:程序博客网 时间:2024/04/27 01:07
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;}@Overrideprotected 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值}}@Overrideprotected 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() {@Overridepublic void onClick(View v) {return;}});flowLayout.addView(textView);}scrollView.addView(flowLayout);
运行结果:



0 0