自定义控件之流式布局FlowLayout

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);
运行结果:



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值