自定义Android流布局实现推荐标签功能

来源:互联网 发布:betterzip mac 密钥 编辑:程序博客网 时间:2024/05/16 14:48

之前遇到需要用自动换行的流布局来实现推荐标签的功能,当时为这个问题弄得焦头烂额,找了好多代码都没能完美解决这个问题,不过最终还是搞定了。

我想要实现的就是这种效果,最开始是用的LinearLayout布局,但发现当标签过多时没办法自动换行,没办法只能自定义控件来实现了。

下面直接上代码

public class FlowLayout extends ViewGroup {public FlowLayout(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}public FlowLayout(Context context, AttributeSet attrs) {this(context, attrs, 0);}public FlowLayout(Context context) {this(context, null);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);int modeWidth = MeasureSpec.getMode(widthMeasureSpec);int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);int modeHeight = MeasureSpec.getMode(heightMeasureSpec);// wrap_contentint width = 0;int height = 0;// 记录每一行的宽度与高度int lineWidth = 0;int lineHeight = 0;// 得到内部元素的个数int cCount = getChildCount();for (int i = 0; i < cCount; i++) {View child = getChildAt(i);// 测量子View的宽和高measureChild(child, widthMeasureSpec, heightMeasureSpec);// 得到LayoutParamsMarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();// 子View占据的宽度int childWidth = child.getMeasuredWidth() + lp.leftMargin+ lp.rightMargin;// 子View占据的高度int childHeight = child.getMeasuredHeight() + lp.topMargin+ lp.bottomMargin;// 换行if (lineWidth + childWidth > sizeWidth - getPaddingLeft()- getPaddingRight()) {// 对比得到最大的宽度width = Math.max(width, lineWidth);// 重置lineWidthlineWidth = childWidth;// 记录行高height += lineHeight;lineHeight = childHeight;} else// 未换行{// 叠加行宽lineWidth += childWidth;// 得到当前行最大的高度lineHeight = Math.max(lineHeight, childHeight);}// 最后一个控件if (i == cCount - 1) {width = Math.max(lineWidth, width);height += lineHeight;}}Log.e("TAG", "sizeWidth = " + sizeWidth);Log.e("TAG", "sizeHeight = " + sizeHeight);setMeasuredDimension(//modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width+ getPaddingLeft() + getPaddingRight(),modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height+ getPaddingTop() + getPaddingBottom()//);}/** * 存储所有的View */private List<List<View>> mAllViews = new ArrayList<List<View>>();/** * 每一行的高度 */private List<Integer> mLineHeight = new ArrayList<Integer>();@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {mAllViews.clear();mLineHeight.clear();// 当前ViewGroup的宽度int width = getWidth();int lineWidth = 0;int lineHeight = 0;List<View> lineViews = new ArrayList<View>();int cCount = getChildCount();for (int i = 0; i < cCount; i++) {View child = getChildAt(i);MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();int childWidth = child.getMeasuredWidth();int childHeight = child.getMeasuredHeight();// 如果需要换行if (childWidth + lineWidth + lp.leftMargin + lp.rightMargin > width- getPaddingLeft() - getPaddingRight()) {// 记录LineHeightmLineHeight.add(lineHeight);// 记录当前行的ViewsmAllViews.add(lineViews);// 重置我们的行宽和行高lineWidth = 0;lineHeight = childHeight + lp.topMargin + lp.bottomMargin;// 重置我们的View集合lineViews = new ArrayList<View>();}lineWidth += childWidth + lp.leftMargin + lp.rightMargin;lineHeight = Math.max(lineHeight, childHeight + lp.topMargin+ lp.bottomMargin);lineViews.add(child);}// for end// 处理最后一行mLineHeight.add(lineHeight);mAllViews.add(lineViews);// 设置子View的位置int left = getPaddingLeft();int top = getPaddingTop();// 行数int lineNum = mAllViews.size();for (int i = 0; i < lineNum; i++) {// 当前行的所有的ViewlineViews = mAllViews.get(i);lineHeight = mLineHeight.get(i);for (int j = 0; j < lineViews.size(); j++) {View child = lineViews.get(j);// 判断child的状态if (child.getVisibility() == View.GONE) {continue;}MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();int lc = left + lp.leftMargin;int tc = top + lp.topMargin;int rc = lc + child.getMeasuredWidth();int bc = tc + child.getMeasuredHeight();// 为子View进行布局child.layout(lc, tc, rc, bc);left += child.getMeasuredWidth() + lp.leftMargin+ lp.rightMargin;}left = getPaddingLeft();top += lineHeight;}}/** * 与当前ViewGroup对应的LayoutParams */@Overridepublic LayoutParams generateLayoutParams(AttributeSet attrs) {return new MarginLayoutParams(getContext(), attrs);}}
在我们的项目中使用的时候自然要动态的来添加标签

  <com.demo.view.FlowLayout                android:id="@+id/type"                android:layout_width="match_parent"                android:layout_height="wrap_content" >            </com.demo.view.FlowLayout>
private FlowLayout type;

type = (FlowLayout) findViewById(R.id.type);

initData(BaseData.types, type);

private void initData(String[] data, FlowLayout layout) {LayoutInflater inflater = LayoutInflater.from(this);for (int i = 0; i < data.length; i++) {TextView tv = (TextView) inflater.inflate(R.layout.tv, layout,false);tv.setText(data[i]);layout.addView(tv);}}

下面是tv.xml

<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="wrap_content"    android:layout_height="wrap_content"   android:layout_margin="5dp"    android:background="@drawable/tv_bg" ></TextView>

这样就完美实现了一个自动换行的流布局。

参考自视频打造Android流式布局和热门标签


1 0
原创粉丝点击