实现结果就是类似标签流
首先 自定义的类
public class FlowLayout extends ViewGroup { private static final String TAG = "FlowLayout"; /** * 存储所有的View,按行记录 */ private List> mAllViews = new ArrayList
>(); /** * 记录每一行的最大高度 */ private List
mLineHeight = new ArrayList (); private Context mContext; public FlowLayout(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; } public FlowLayout(Context context) { super(context); mContext = context; } /** * 负责设置子控件的测量模式和大小 根据所有子控件设置自己的宽和高 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 获得它的父容器为它设置的测量模式和大小 int sizeWidth = View.MeasureSpec.getSize(widthMeasureSpec); int sizeHeight = View.MeasureSpec.getSize(heightMeasureSpec); int modeWidth = View.MeasureSpec.getMode(widthMeasureSpec); int modeHeight = View.MeasureSpec.getMode(heightMeasureSpec); Log.e(TAG, sizeWidth + "," + sizeHeight); // 如果是warp_content情况下,记录宽和高 int width = 0; int height = 0; /** * 记录每一行的宽度,width不断取最大宽度 */ int lineWidth = 0; /** * 每一行的高度,累加至height */ int lineHeight = 0; int cCount = getChildCount(); // 遍历每个子元素 for (int i = 0; i < cCount; i++) { View child = getChildAt(i); View childs = getChildAt(i); // 测量每一个child的宽和高 measureChild(child, widthMeasureSpec, heightMeasureSpec); // 得到child的lp ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) child .getLayoutParams(); // 当前子空间实际占据的宽度 int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; // 当前子空间实际占据的高度 int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; /** * 如果加入当前child,则超出最大宽度,则的到目前最大宽度给width,类加height 然后开启新行 */ if (lineWidth + childWidth > sizeWidth) { width = Math.max(lineWidth, childWidth);// 取最大的 lineWidth = childWidth; // 重新开启新行,开始记录 // 叠加当前高度, height += lineHeight; // 开启记录下一行的高度 lineHeight = childHeight; } else // 否则累加值lineWidth,lineHeight取最大高度 { lineWidth += childWidth; lineHeight = Math.max(lineHeight, childHeight); } // 如果是最后一个,则将当前记录的最大宽度和当前lineWidth做比较 if (i == cCount - 1) { width = Math.max(width, lineWidth); height += lineHeight; } } setMeasuredDimension((modeWidth == View.MeasureSpec.EXACTLY) ? sizeWidth : width, (modeHeight == View.MeasureSpec.EXACTLY) ? sizeHeight : height); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { mAllViews.clear(); mLineHeight.clear(); int width = getWidth(); int lineWidth = 0; int lineHeight = 0; // 存储每一行所有的childView List lineViews = new ArrayList (); int cCount = getChildCount(); // 遍历所有的孩子 for (int i = 0; i < cCount; i++) { View child = getChildAt(i); ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) child .getLayoutParams(); int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); // 如果已经需要换行 if (childWidth + lp.leftMargin + lp.rightMargin + lineWidth > width) { // 记录这一行所有的View以及最大高度 mLineHeight.add(lineHeight); // 将当前行的childView保存,然后开启新的ArrayList保存下一行的childView mAllViews.add(lineViews); lineWidth = 0;// 重置行宽 lineViews = new ArrayList (); } /** * 如果不需要换行,则累加 */ lineWidth += childWidth + lp.leftMargin + lp.rightMargin; lineHeight = Math.max(lineHeight, childHeight + lp.topMargin + lp.bottomMargin); lineViews.add(child); } // 记录最后一行 mLineHeight.add(lineHeight); mAllViews.add(lineViews); int left = 0; int top = 0; // 得到总行数 int lineNums = mAllViews.size(); for (int i = 0; i < lineNums; i++) { // 每一行的所有的views lineViews = mAllViews.get(i); // 当前行的最大高度 lineHeight = mLineHeight.get(i); Log.e(TAG, "第" + i + "行 :" + lineViews.size() + " , " + lineViews); Log.e(TAG, "第" + i + "行, :" + lineHeight); // 遍历当前行所有的View for (int j = 0; j < lineViews.size(); j++) { View child = lineViews.get(j); if (child.getVisibility() == View.GONE) { continue; } ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) child .getLayoutParams(); //计算childView的left,top,right,bottom int lc = left + lp.leftMargin; int tc = top + lp.topMargin; int rc = lc + child.getMeasuredWidth(); int bc = tc + child.getMeasuredHeight(); Log.e(TAG, child + " , l = " + lc + " , t = " + t + " , r =" + rc + " , b = " + bc); child.layout(lc, tc, rc, bc); left += child.getMeasuredWidth() + lp.rightMargin + lp.leftMargin; } left = 0; top += lineHeight; } } /** * 返回当前ViewGroup相关联的LayoutParams */ @Override public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { return new ViewGroup.MarginLayoutParams(getContext(), attrs); } public TextView addView(String tags, int bgcolor) { if (BussinessUtil.isValid(tags)) { LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); params.setMargins(0, 0, 20, 30);//左上右下 TextView textView = new TextView(this.getContext()); textView.setGravity(Gravity.CENTER); textView.setBackgroundResource(bgcolor); textView.setTextColor(getResources().getColor(R.color.search_flowlayout_textcolor)); textView.setSelected(false); textView.setTextSize(13); textView.setPadding(50, 20, 50, 20); textView.setSingleLine(); textView.setEllipsize(TextUtils.TruncateAt.valueOf("END")); textView.setText(tags); this.addView(textView, params); return textView; } return null; } public View addHitoryView(String tags, int bgcolor) { View view = null; if (!TextUtils.isEmpty(tags)) { LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); params.setMargins(0, 0, 20, 30);//左上右下 view = LayoutInflater.from(mContext).inflate(R.layout.item_custom_search_key, null); ((TextView) view.findViewById(R.id.tvSearchKey)).setText(tags); this.addView(view, params); } return view; } }
其次是自定义控件使用的布局
xml version="1.0" encoding="utf-8"?>xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content"> android:id="@+id/tvSearchKey" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/search_flowlayout_textcolor" android:text="护肤" android:background="@drawable/history_tag_bg_selector" android:paddingLeft="@dimen/margin_fifteen" android:paddingRight="@dimen/margin_fifteen" android:paddingTop="@dimen/margin_five" android:paddingBottom="@dimen/margin_five" /> android:id="@+id/ivDelHotkey" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/icon_search_delete" android:layout_alignRight="@+id/tvSearchKey" android:visibility="gone"/>
然后 在需要的地方直接使用
android:id="@+id/rlCaizhuangKeywordLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:paddingBottom="-5dp">
android:id="@+id/flCaizhuangSearchTag"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="@dimen/margin_eight"
android:layout_marginRight="@dimen/margin_eight"
android:layout_marginTop="@dimen/margin_twenty" />
java代码中使用,和普通控件一样,首先 通过finfviewbyid 找到这个控件
然后,为他初始化数据,这里模拟创建了几个数据,然后通过for循环依次添加进去,并且可以为他设置点击时候的颜色变化
private void initKeyWordData() { caizhuangKeywords = new ArrayList<>(); caizhuangKeywords.add("春"); caizhuangKeywords.add("学院风"); caizhuangKeywords.add("时尚"); caizhuangKeywords.add("原宿style"); caizhuangKeywords.add("清新"); caizhuangKeywords.add("约会装"); for (String caizhuangKeyword : caizhuangKeywords) { final TextView tvCaizhuangKeyword = flCaizhuangSearchTag.addView(caizhuangKeyword, R.drawable.caizhuang_keyword_tag_bg_selector); tvCaizhuangKeyword.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ToastUtil.showToast(tvCaizhuangKeyword.getText().toString() + ""); } }); } }
其中的caizhuang_keyword_tag_bg_selector就是为标签设计的点击颜色改变的drawable 具体如下:
xml version="1.0" encoding="utf-8"?>xmlns:android="http://schemas.android.com/apk/res/android"> - android:state_pressed="true">
android:shape="rectangle"> android:color="@color/basic_pink_color" /> android:width="1px" android:color="@color/mess_notify_titlecolor" /> android:radius="@dimen/margin_five" /> - android:state_pressed="false">
android:shape="rectangle"> android:color="@color/caizhuang_keyword_defaule_bgcolor" /> android:width="1px" android:color="@color/login_line_color" /> android:radius="@dimen/margin_five" />
需要注意的是,如果仅仅添加上了这个drawable,但是并未在java代码中为其添加点击事件,那么这个drawable的press选择器是不会起作用的,所以,只有添加了onClicklistener等点击事件,这个drawable才会出效果,这是本人开发过程时没有注意到的细节被困扰了一下,特整理出来提醒小伙伴们。