前言:可以设置标签的选中效果。 可以设置标签的选中类型:不可选中、单选、限数量多选和不限数量多选等, 并支持设置必选项等功能

1、效果图

2、关键代码:LabelsView.java

import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import word.hello.com.module.R;/*** 标签列表*/
public class LabelsView extends ViewGroup implements View.OnClickListener {private Context mContext;private ColorStateList mTextColor;private float mTextSize;private Drawable mLabelBg;private int mTextPaddingLeft;private int mTextPaddingTop;private int mTextPaddingRight;private int mTextPaddingBottom;private int mWordMargin;private int mLineMargin;private SelectType mSelectType;private int mMaxSelect;//用于保存label数据的keyprivate static final int KEY_DATA = R.id.labels_data;//用于保存label位置的keyprivate static final int KEY_POSITION = R.id.labels_position;private ArrayList<Object> mLabels = new ArrayList<>();//保存选中的label的位置private ArrayList<Integer> mSelectLabels = new ArrayList<>();//保存必选项。在多选模式下,可以设置必选项,必选项默认选中,不能反选private ArrayList<Integer> mCompulsorys = new ArrayList<>();private OnLabelClickListener mLabelClickListener;private OnLabelSelectChangeListener mLabelSelectChangeListener;/*** Label的选择类型*/public enum SelectType {//不可选中,也不响应选中事件回调。(默认)NONE(1),//单选,可以反选。SINGLE(2),//单选,不可以反选。这种模式下,至少有一个是选中的,默认是第一个SINGLE_IRREVOCABLY(3),//多选MULTI(4);int value;SelectType(int value) {this.value = value;}static SelectType get(int value) {switch (value) {case 1:return NONE;case 2:return SINGLE;case 3:return SINGLE_IRREVOCABLY;case 4:return MULTI;}return NONE;}}public LabelsView(Context context) {super(context);mContext = context;}public LabelsView(Context context, AttributeSet attrs) {super(context, attrs);mContext = context;getAttrs(context, attrs);}public LabelsView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);mContext = context;getAttrs(context, attrs);}private void getAttrs(Context context, AttributeSet attrs) {if (attrs != null) {TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.labels_view);int type = mTypedArray.getInt(R.styleable.labels_view_selectType, 1);mSelectType = SelectType.get(type);mMaxSelect = mTypedArray.getInteger(R.styleable.labels_view_maxSelect, 0);mTextColor = mTypedArray.getColorStateList(R.styleable.labels_view_labelTextColor);mTextSize = mTypedArray.getDimension(R.styleable.labels_view_labelTextSize,sp2px(context, 14));mTextPaddingLeft = mTypedArray.getDimensionPixelOffset(R.styleable.labels_view_labelTextPaddingLeft, 0);mTextPaddingTop = mTypedArray.getDimensionPixelOffset(R.styleable.labels_view_labelTextPaddingTop, 0);mTextPaddingRight = mTypedArray.getDimensionPixelOffset(R.styleable.labels_view_labelTextPaddingRight, 0);mTextPaddingBottom = mTypedArray.getDimensionPixelOffset(R.styleable.labels_view_labelTextPaddingBottom, 0);mLineMargin = mTypedArray.getDimensionPixelOffset(R.styleable.labels_view_lineMargin, 0);mWordMargin = mTypedArray.getDimensionPixelOffset(R.styleable.labels_view_wordMargin, 0);int labelBgResId = mTypedArray.getResourceId(R.styleable.labels_view_labelBackground, 0);mLabelBg = getResources().getDrawable(labelBgResId);mTypedArray.recycle();}}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int count = getChildCount();int maxWidth = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();int contentHeight = 0; //记录内容的高度int lineWidth = 0; //记录行的宽度int maxLineWidth = 0; //记录最宽的行宽int maxItemHeight = 0; //记录一行中item高度最大的高度boolean begin = true; //是否是行的开头for (int i = 0; i < count; i++) {View view = getChildAt(i);measureChild(view, widthMeasureSpec, heightMeasureSpec);if (!begin) {lineWidth += mWordMargin;} else {begin = false;}if (maxWidth <= lineWidth + view.getMeasuredWidth()) {contentHeight += mLineMargin;contentHeight += maxItemHeight;maxItemHeight = 0;maxLineWidth = Math.max(maxLineWidth, lineWidth);lineWidth = 0;begin = true;}maxItemHeight = Math.max(maxItemHeight, view.getMeasuredHeight());lineWidth += view.getMeasuredWidth();}contentHeight += maxItemHeight;maxLineWidth = Math.max(maxLineWidth, lineWidth);setMeasuredDimension(measureWidth(widthMeasureSpec, maxLineWidth),measureHeight(heightMeasureSpec, contentHeight));}private int measureWidth(int measureSpec, int contentWidth) {int result = 0;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);if (specMode == MeasureSpec.EXACTLY) {result = specSize;} else {result = contentWidth + getPaddingLeft() + getPaddingRight();if (specMode == MeasureSpec.AT_MOST) {result = Math.min(result, specSize);}}result = Math.max(result, getSuggestedMinimumWidth());return result;}private int measureHeight(int measureSpec, int contentHeight) {int result = 0;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);if (specMode == MeasureSpec.EXACTLY) {result = specSize;} else {result = contentHeight + getPaddingTop() + getPaddingBottom();if (specMode == MeasureSpec.AT_MOST) {result = Math.min(result, specSize);}}result = Math.max(result, getSuggestedMinimumHeight());return result;}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {int x = getPaddingLeft();int y = getPaddingTop();int contentWidth = right - left;int maxItemHeight = 0;int count = getChildCount();for (int i = 0; i < count; i++) {View view = getChildAt(i);if (contentWidth < x + view.getMeasuredWidth() + getPaddingRight()) {x = getPaddingLeft();y += mLineMargin;y += maxItemHeight;maxItemHeight = 0;}view.layout(x, y, x + view.getMeasuredWidth(), y + view.getMeasuredHeight());x += view.getMeasuredWidth();x += mWordMargin;maxItemHeight = Math.max(maxItemHeight, view.getMeasuredHeight());}}/*  用于保存View的信息的key  */private static final String KEY_SUPER_STATE = "key_super_state";private static final String KEY_TEXT_COLOR_STATE = "key_text_color_state";private static final String KEY_TEXT_SIZE_STATE = "key_text_size_state";private static final String KEY_BG_RES_ID_STATE = "key_bg_res_id_state";private static final String KEY_PADDING_STATE = "key_padding_state";private static final String KEY_WORD_MARGIN_STATE = "key_word_margin_state";private static final String KEY_LINE_MARGIN_STATE = "key_line_margin_state";private static final String KEY_SELECT_TYPE_STATE = "key_select_type_state";private static final String KEY_MAX_SELECT_STATE = "key_max_select_state";// 由于新版(1.4.0)的标签列表允许设置任何类型的数据,而不仅仅是String。并且标签显示的内容// 最终由LabelTextProvider提供,所以LabelsView不再在onSaveInstanceState()和onRestoreInstanceState()// 中保存和恢复标签列表的数据。private static final String KEY_LABELS_STATE = "key_labels_state";private static final String KEY_SELECT_LABELS_STATE = "key_select_labels_state";private static final String KEY_COMPULSORY_LABELS_STATE = "key_select_compulsory_state";@Overrideprotected Parcelable onSaveInstanceState() {Bundle bundle = new Bundle();//保存父类的信息bundle.putParcelable(KEY_SUPER_STATE, super.onSaveInstanceState());//保存标签文字颜色if (mTextColor != null) {bundle.putParcelable(KEY_TEXT_COLOR_STATE, mTextColor);}//保存标签文字大小bundle.putFloat(KEY_TEXT_SIZE_STATE, mTextSize);//保存标签背景 (由于背景改用Drawable,所以不能自动保存和恢复)
//        bundle.putInt(KEY_BG_RES_ID_STATE, mLabelBgResId);//保存标签内边距bundle.putIntArray(KEY_PADDING_STATE, new int[]{mTextPaddingLeft, mTextPaddingTop,mTextPaddingRight, mTextPaddingBottom});//保存标签间隔bundle.putInt(KEY_WORD_MARGIN_STATE, mWordMargin);//保存行间隔bundle.putInt(KEY_LINE_MARGIN_STATE, mLineMargin);//保存标签的选择类型bundle.putInt(KEY_SELECT_TYPE_STATE, mSelectType.value);//保存标签的最大选择数量bundle.putInt(KEY_MAX_SELECT_STATE, mMaxSelect);//保存标签列表
//        if (!mLabels.isEmpty()) {
//            bundle.putStringArrayList(KEY_LABELS_STATE, mLabels);
//        }//保存已选择的标签列表if (!mSelectLabels.isEmpty()) {bundle.putIntegerArrayList(KEY_SELECT_LABELS_STATE, mSelectLabels);}//保存必选项列表if (!mCompulsorys.isEmpty()) {bundle.putIntegerArrayList(KEY_COMPULSORY_LABELS_STATE, mCompulsorys);}return bundle;}@Overrideprotected void onRestoreInstanceState(Parcelable state) {if (state instanceof Bundle) {Bundle bundle = (Bundle) state;//恢复父类信息super.onRestoreInstanceState(bundle.getParcelable(KEY_SUPER_STATE));//恢复标签文字颜色ColorStateList color = bundle.getParcelable(KEY_TEXT_COLOR_STATE);if (color != null) {setLabelTextColor(color);}//恢复标签文字大小setLabelTextSize(bundle.getFloat(KEY_TEXT_SIZE_STATE, mTextSize));
//            //恢复标签背景  (由于背景改用Drawable,所以不能自动保存和恢复)
//            int resId = bundle.getInt(KEY_BG_RES_ID_STATE, mLabelBgResId);
//            if (resId != 0) {
//                setLabelBackgroundResource(resId);
//            }//恢复标签内边距int[] padding = bundle.getIntArray(KEY_PADDING_STATE);if (padding != null && padding.length == 4) {setLabelTextPadding(padding[0], padding[1], padding[2], padding[3]);}//恢复标签间隔setWordMargin(bundle.getInt(KEY_WORD_MARGIN_STATE, mWordMargin));//恢复行间隔setLineMargin(bundle.getInt(KEY_LINE_MARGIN_STATE, mLineMargin));//恢复标签的选择类型setSelectType(SelectType.get(bundle.getInt(KEY_SELECT_TYPE_STATE, mSelectType.value)));//恢复标签的最大选择数量setMaxSelect(bundle.getInt(KEY_MAX_SELECT_STATE, mMaxSelect));
//            //恢复标签列表
//            ArrayList<String> labels = bundle.getStringArrayList(KEY_LABELS_STATE);
//            if (labels != null && !labels.isEmpty()) {
//                setLabels(labels);
//            }//恢复必选项列表ArrayList<Integer> compulsory = bundle.getIntegerArrayList(KEY_COMPULSORY_LABELS_STATE);if (compulsory != null && !compulsory.isEmpty()) {setCompulsorys(compulsory);}//恢复已选择的标签列表ArrayList<Integer> selectLabel = bundle.getIntegerArrayList(KEY_SELECT_LABELS_STATE);if (selectLabel != null && !selectLabel.isEmpty()) {int size = selectLabel.size();int[] positions = new int[size];for (int i = 0; i < size; i++) {positions[i] = selectLabel.get(i);}setSelects(positions);}return;}super.onRestoreInstanceState(state);}/*** 设置标签列表** @param labels*/public void setLabels(List<String> labels) {setLabels(labels, new LabelTextProvider<String>() {@Overridepublic CharSequence getLabelText(TextView label, int position, String data) {return data.trim();}});}/*** 设置标签列表,标签列表的数据可以是任何类型的数据,* 它最终显示的内容由LabelTextProvider根据标签的数据提供。** @param labels* @param provider* @param <T>*/public <T> void setLabels(List<T> labels, LabelTextProvider<T> provider) {//清空原有的标签innerClearAllSelect();removeAllViews();mLabels.clear();if (labels != null) {mLabels.addAll(labels);int size = labels.size();for (int i = 0; i < size; i++) {addLabel(labels.get(i), i, provider);}}if (mSelectType == SelectType.SINGLE_IRREVOCABLY) {setSelects(0);}}/*** 获取标签列表** @return*/public <T> List<T> getLabels() {return (List<T>) mLabels;}private <T> void addLabel(T data, int position, LabelTextProvider<T> provider) {final TextView label = new TextView(mContext);label.setPadding(mTextPaddingLeft, mTextPaddingTop, mTextPaddingRight, mTextPaddingBottom);label.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);label.setTextColor(mTextColor != null ? mTextColor : ColorStateList.valueOf(0xFF000000));//设置给label的背景(Drawable)是一个Drawable对象的拷贝,// 因为如果所有的标签都共用一个Drawable对象,会引起背景错乱。label.setBackgroundDrawable(mLabelBg.getConstantState().newDrawable());//label通过tag保存自己的数据(data)和位置(position)label.setTag(KEY_DATA, data);label.setTag(KEY_POSITION, position);label.setOnClickListener(this);addView(label);label.setText(provider.getLabelText(label, position, data));}@Overridepublic void onClick(View v) {if (v instanceof TextView) {TextView label = (TextView) v;if (mSelectType != SelectType.NONE) {if (label.isSelected()) {if (mSelectType != SelectType.SINGLE_IRREVOCABLY&& !mCompulsorys.contains((Integer) label.getTag(KEY_POSITION))) {setLabelSelect(label, false);}} else if (mSelectType == SelectType.SINGLE || mSelectType == SelectType.SINGLE_IRREVOCABLY) {innerClearAllSelect();setLabelSelect(label, true);} else if (mSelectType == SelectType.MULTI&& (mMaxSelect <= 0 || mMaxSelect > mSelectLabels.size())) {setLabelSelect(label, true);}}if (mLabelClickListener != null) {mLabelClickListener.onLabelClick(label, label.getTag(KEY_DATA), (int) label.getTag(KEY_POSITION));}}}private void setLabelSelect(TextView label, boolean isSelect) {if (label.isSelected() != isSelect) {label.setSelected(isSelect);if (isSelect) {mSelectLabels.add((Integer) label.getTag(KEY_POSITION));} else {mSelectLabels.remove((Integer) label.getTag(KEY_POSITION));}if (mLabelSelectChangeListener != null) {mLabelSelectChangeListener.onLabelSelectChange(label, label.getTag(KEY_DATA),isSelect, (int) label.getTag(KEY_POSITION));}}}/*** 取消所有选中的label*/public void clearAllSelect() {if (mSelectType != SelectType.SINGLE_IRREVOCABLY) {if (mSelectType == SelectType.MULTI && !mCompulsorys.isEmpty()) {clearNotCompulsorySelect();} else {innerClearAllSelect();}}}private void innerClearAllSelect() {int count = getChildCount();for (int i = 0; i < count; i++) {setLabelSelect((TextView) getChildAt(i), false);}mSelectLabels.clear();}private void clearNotCompulsorySelect() {int count = getChildCount();List<Integer> temps = new ArrayList<>();for (int i = 0; i < count; i++) {if (!mCompulsorys.contains(i)) {setLabelSelect((TextView) getChildAt(i), false);temps.add(i);}}mSelectLabels.removeAll(temps);}/*** 设置选中label** @param positions*/public void setSelects(List<Integer> positions) {if (positions != null) {int size = positions.size();int[] ps = new int[size];for (int i = 0; i < size; i++) {ps[i] = positions.get(i);}setSelects(ps);}}/*** 设置选中label** @param positions*/public void setSelects(int... positions) {if (mSelectType != SelectType.NONE) {ArrayList<TextView> selectLabels = new ArrayList<>();int count = getChildCount();int size = mSelectType == SelectType.SINGLE || mSelectType == SelectType.SINGLE_IRREVOCABLY? 1 : mMaxSelect;for (int p : positions) {if (p < count) {TextView label = (TextView) getChildAt(p);if (!selectLabels.contains(label)) {setLabelSelect(label, true);selectLabels.add(label);}if (size > 0 && selectLabels.size() == size) {break;}}}for (int i = 0; i < count; i++) {TextView label = (TextView) getChildAt(i);if (!selectLabels.contains(label)) {setLabelSelect(label, false);}}}}/*** 设置必选项,只有在多项模式下,这个方法才有效** @param positions*/public void setCompulsorys(List<Integer> positions) {if (mSelectType == SelectType.MULTI && positions != null) {mCompulsorys.clear();mCompulsorys.addAll(positions);//必选项发生改变,就要恢复到初始状态。innerClearAllSelect();setSelects(positions);}}/*** 设置必选项,只有在多项模式下,这个方法才有效** @param positions*/public void setCompulsorys(int... positions) {if (mSelectType == SelectType.MULTI && positions != null) {List<Integer> ps = new ArrayList<>(positions.length);for (int i : positions) {ps.add(i);}setCompulsorys(ps);}}/*** 获取必选项,** @return*/public List<Integer> getCompulsorys() {return mCompulsorys;}/*** 清空必选项,只有在多项模式下,这个方法才有效*/public void clearCompulsorys() {if (mSelectType == SelectType.MULTI && !mCompulsorys.isEmpty()) {mCompulsorys.clear();//必选项发生改变,就要恢复到初始状态。innerClearAllSelect();}}/*** 获取选中的label(返回的是所有选中的标签的位置)** @return*/public List<Integer> getSelectLabels() {return mSelectLabels;}/*** 获取选中的label(返回的是所头选中的标签的数据)** @param <T>* @return*/public <T> List<T> getSelectLabelDatas() {List<T> list = new ArrayList<>();int size = mSelectLabels.size();for (int i = 0; i < size; i++) {View label = getChildAt(mSelectLabels.get(i));Object data = label.getTag(KEY_DATA);if (data != null) {list.add((T) data);}}return list;}/*** 设置标签背景** @param resId*/public void setLabelBackgroundResource(int resId) {setLabelBackgroundDrawable(getResources().getDrawable(resId));}/*** 设置标签背景** @param color*/public void setLabelBackgroundColor(int color) {setLabelBackgroundDrawable(new ColorDrawable(color));}/*** 设置标签背景** @param drawable*/public void setLabelBackgroundDrawable(Drawable drawable) {mLabelBg = drawable;int count = getChildCount();for (int i = 0; i < count; i++) {TextView label = (TextView) getChildAt(i);label.setBackgroundDrawable(mLabelBg.getConstantState().newDrawable());}}/*** 设置标签内边距** @param left* @param top* @param right* @param bottom*/public void setLabelTextPadding(int left, int top, int right, int bottom) {if (mTextPaddingLeft != left || mTextPaddingTop != top|| mTextPaddingRight != right || mTextPaddingBottom != bottom) {mTextPaddingLeft = left;mTextPaddingTop = top;mTextPaddingRight = right;mTextPaddingBottom = bottom;int count = getChildCount();for (int i = 0; i < count; i++) {TextView label = (TextView) getChildAt(i);label.setPadding(left, top, right, bottom);}}}public int getTextPaddingLeft() {return mTextPaddingLeft;}public int getTextPaddingTop() {return mTextPaddingTop;}public int getTextPaddingRight() {return mTextPaddingRight;}public int getTextPaddingBottom() {return mTextPaddingBottom;}/*** 设置标签的文字大小(单位是px)** @param size*/public void setLabelTextSize(float size) {if (mTextSize != size) {mTextSize = size;int count = getChildCount();for (int i = 0; i < count; i++) {TextView label = (TextView) getChildAt(i);label.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);}}}public float getLabelTextSize() {return mTextSize;}/*** 设置标签的文字颜色** @param color*/public void setLabelTextColor(int color) {setLabelTextColor(ColorStateList.valueOf(color));}/*** 设置标签的文字颜色** @param color*/public void setLabelTextColor(ColorStateList color) {mTextColor = color;int count = getChildCount();for (int i = 0; i < count; i++) {TextView label = (TextView) getChildAt(i);label.setTextColor(mTextColor != null ? mTextColor : ColorStateList.valueOf(0xFF000000));}}public ColorStateList getLabelTextColor() {return mTextColor;}/*** 设置行间隔*/public void setLineMargin(int margin) {if (mLineMargin != margin) {mLineMargin = margin;requestLayout();}}public int getLineMargin() {return mLineMargin;}/*** 设置标签的间隔*/public void setWordMargin(int margin) {if (mWordMargin != margin) {mWordMargin = margin;requestLayout();}}public int getWordMargin() {return mWordMargin;}/*** 设置标签的选择类型** @param selectType*/public void setSelectType(SelectType selectType) {if (mSelectType != selectType) {mSelectType = selectType;//选择类型发生改变,就要恢复到初始状态。innerClearAllSelect();if (mSelectType == SelectType.SINGLE_IRREVOCABLY) {setSelects(0);}if (mSelectType != SelectType.MULTI) {mCompulsorys.clear();}}}public SelectType getSelectType() {return mSelectType;}/*** 设置最大的选择数量** @param maxSelect*/public void setMaxSelect(int maxSelect) {if (mMaxSelect != maxSelect) {mMaxSelect = maxSelect;if (mSelectType == SelectType.MULTI) {//最大选择数量发生改变,就要恢复到初始状态。innerClearAllSelect();}}}public int getMaxSelect() {return mMaxSelect;}/*** 设置标签的点击监听** @param l*/public void setOnLabelClickListener(OnLabelClickListener l) {mLabelClickListener = l;}/*** 设置标签的选择监听** @param l*/public void setOnLabelSelectChangeListener(OnLabelSelectChangeListener l) {mLabelSelectChangeListener = l;}/*** sp转px*/public static int sp2px(Context context, float spVal) {return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,spVal, context.getResources().getDisplayMetrics());}public interface OnLabelClickListener {/*** @param label    标签* @param data     标签对应的数据* @param position 标签位置*/void onLabelClick(TextView label, Object data, int position);}public interface OnLabelSelectChangeListener {/*** @param label    标签* @param data     标签对应的数据* @param isSelect 是否选中* @param position 标签位置*/void onLabelSelectChange(TextView label, Object data, boolean isSelect, int position);}/*** 给标签提供最终需要显示的数据。因为LabelsView的列表可以设置任何类型的数据,而LabelsView里的每个item的是一* 个TextView,只能显示CharSequence的数据,所以LabelTextProvider需要根据每个item的数据返回item最终要显示* 的CharSequence。** @param <T>*/public interface LabelTextProvider<T> {/*** 根据data和position返回label需要需要显示的数据。** @param label* @param position* @param data* @return*/CharSequence getLabelText(TextView label, int position, T data);}}

对应style:

<!--标签列表样式--><declare-styleable name="labels_view"><attr name="selectType" format="enum"><enum name="NONE" value="1" /><enum name="SINGLE" value="2" /><enum name="SINGLE_IRREVOCABLY" value="3" /><enum name="MULTI" value="4" /></attr><attr name="maxSelect" format="integer" /><attr name="labelTextColor" format="reference" /><attr name="labelTextSize" format="dimension" /><attr name="labelTextPaddingLeft" format="dimension" /><attr name="labelTextPaddingTop" format="dimension" /><attr name="labelTextPaddingRight" format="dimension" /><attr name="labelTextPaddingBottom" format="dimension" /><attr name="lineMargin" format="dimension" /><attr name="wordMargin" format="dimension" /><attr name="labelBackground" format="reference" /></declare-styleable>

3、使用方法

public class LabelsViewActivity extends AppCompatActivity implements View.OnClickListener {private LabelsView labelsView;private TextView text1;private List<String> stringList = new ArrayList<>();private List<LabelsDto> listData = new ArrayList<>();private String[] labelsData = {"咖啡", "饮食", "男装", "女装", "眼镜", "内衣配饰", "母婴","鞋靴", "运动", "箱包"};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_labels_view);labelsView = this.findViewById(R.id.labelsView);text1 = this.findViewById(R.id.text1);findViewById(R.id.button1).setOnClickListener(this);findViewById(R.id.button2).setOnClickListener(this);findViewById(R.id.button3).setOnClickListener(this);findViewById(R.id.button4).setOnClickListener(this);initData();}/*** 初始化数据源*/private void initData() {//①:普通String类型for (int i = 0; i < labelsData.length; i++) {stringList.add(labelsData[i]);}//②:标签带ID类型等//标题和idlistData.add(new LabelsDto("近视眼镜", "1"));listData.add(new LabelsDto("精品男装", "2"));listData.add(new LabelsDto("女裙", "3"));listData.add(new LabelsDto("男鞋", "4"));listData.add(new LabelsDto("笔记本", "5"));listData.add(new LabelsDto("生活用品", "6"));listData.add(new LabelsDto("厨房家具", "7"));listData.add(new LabelsDto("3D数码", "8"));//第一步:设置数据源labelsView.setLabels(listData, new LabelsView.LabelTextProvider<LabelsDto>() {@Overridepublic CharSequence getLabelText(TextView label, int position, LabelsDto data) {return data.getLabelTitle();根据data和position返回label需要显示的数据。}});//第二步:点击事件labelsView.setOnLabelClickListener(new LabelsView.OnLabelClickListener() {@Overridepublic void onLabelClick(TextView label, Object data, int position) {//label是被点击的标签(例:label.getText().toString()),// data是标签所对应的数据,position是标签的位置。text1.setText("点击的标题:" + listData.get(position).getLabelTitle()+ "\n点击的id:" + listData.get(position).getLabelId());}});//第三步:标签选中事件labelsView.setOnLabelSelectChangeListener(new LabelsView.OnLabelSelectChangeListener() {@Overridepublic void onLabelSelectChange(TextView label, Object data, boolean isSelect, int position) {//label是被选中的标签,data是标签所对应的数据,isSelect是是否选中,position是标签的位置}});}@Overridepublic void onClick(View view) {switch (view.getId()) {case R.id.button1:labelsView.setSelectType(LabelsView.SelectType.NONE);break;case R.id.button2:labelsView.setSelectType(LabelsView.SelectType.SINGLE);break;case R.id.button3:labelsView.setSelectType(LabelsView.SelectType.SINGLE_IRREVOCABLY);break;case R.id.button4:labelsView.setSelectType(LabelsView.SelectType.MULTI);break;}}
}

对应实体类:

public class LabelsDto {private String labelTitle;//标签标题private String labelId;//标签idpublic String getLabelTitle() {return labelTitle;}public void setLabelTitle(String labelTitle) {this.labelTitle = labelTitle;}public String getLabelId() {return labelId;}public void setLabelId(String labelId) {this.labelId = labelId;}public LabelsDto(String labelTitle, String labelId) {this.labelTitle = labelTitle;this.labelId = labelId;}
}

对应布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:orientation="horizontal"><Buttonandroid:id="@+id/button1"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text="不可选中" /><Buttonandroid:id="@+id/button2"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text="单选,可以反选" /><Buttonandroid:id="@+id/button3"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text="单选,不可以反选" /><Buttonandroid:id="@+id/button4"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text="多选" /></LinearLayout><word.hello.com.module.view.LabelsViewandroid:id="@+id/labelsView"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="15dp"app:labelBackground="@drawable/label_shape"app:labelTextColor="@drawable/label_text"app:labelTextPaddingBottom="5dp"app:labelTextPaddingLeft="10dp"app:labelTextPaddingRight="10dp"app:labelTextPaddingTop="5dp"app:labelTextSize="16sp"app:lineMargin="10dp"app:maxSelect="3"app:selectType="MULTI"app:wordMargin="10dp" /><TextViewandroid:id="@+id/text1"android:layout_width="match_parent"android:layout_height="wrap_content" />
</LinearLayout>

注意:word.hello.com.module.view.LabelsView(路径更改)

对应两个drawable:label_shape、label_text

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"><!-- 标签选中时的背景 --><item android:state_selected="true"><shape><corners android:radius="10dp" /><solid android:color="@color/home_color" /></shape></item><!-- 标签的正常背景 --><item><shape><stroke android:width="1dp" android:color="@color/home_color" /><corners android:radius="10dp" /><solid android:color="@color/white_bg_color" /></shape></item></selector>
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"><!-- 标签选中时的字体颜色 --><item android:color="@color/white_bg_color" android:state_selected="true" /><!-- 标签未选中时的字体颜色 --><item android:color="@color/home_color" /></selector>

颜色值:

<color name="white_bg_color">#ffffff</color><color name="home_color">#8F40C0</color>

String values中添加:

    <item name="labels_data" type="id" /><item name="labels_position" type="id" />

4、常用方法属性图:

界面描述 方法名
标签选中颜色(可单独设置一种颜色) labelTextColor
标签的背景 labelBackground
字体大小 labelTextSize
标签下边距 labelTextPaddingBottom
标签左边距 labelTextPaddingLeft
标签右边距 labelTextPaddingRight
标签上边距 labelTextPaddingTop
行间距 lineMargin
标签间隔 wordMargin
标签的选择类型 有单选(可反选)、单选(不可反选)、多选、不可选四种类型) selectType
标签的最大选择数量,(多选的时候才有用),0为不限数量 maxSelect

注意:以上均可在代码中动态进行设置

常用方法 方法名
设置选中标签①(可以多个) labelsView.setSelects(1,4,7);
设置选中标签② List integers=new ArrayList<>(); integers.add(1);integers.add(4);integers.add(7);labelsView.setSelects(integers);
获取选中的标签(返回的是所有选中的标签的下标) labelsView.getSelectLabels();
获取选中的label(返回的是所有选中的标签的数据) labelsView.getSelectLabelDatas();
取消所有选中的标签 labelsView.clearAllSelect();
设置必选项①,(模式必须为多项模式) labelsView.setCompulsorys(1,4,7);
设置必选项② 方法同标签②一致
清空必选项(模式必须为多项模式) labelsView.clearCompulsorys();

注意:所有的set方法都有对应的get方法

最后附上原github.地址:https://github.com/donkingliang/LabelsView

安卓标签LabelsView的简单使用相关推荐

  1. JSP自定义标签就是如此简单

    2019独角兽企业重金招聘Python工程师标准>>> 为什么要用到简单标签? 上一篇博客中我已经讲解了传统标签,想要开发自定义标签,大多数情况下都要重写doStartTag(),d ...

  2. 一图秒懂 - MD文件语法标签,最简单的markdown入门

    代码-效果图 https://rudon.blog.csdn.net/article/details/104272292 (完整代码在本文最下面) 实时查看MD文件效果 - 在线Markdown预览 ...

  3. android 模拟器作用,安卓模拟器:看似简单实则用处极大的日常功能

    原标题:安卓模拟器:看似简单实则用处极大的日常功能 逍遥安卓模拟器每个版本都会推出一些实用性比较强大的功能,并不断完善现有功能,为用户带来良好的游戏体验,今天给大家带来的是每天使用率较高的功能完整版解 ...

  4. 火山安卓自定义列表框简单操作教程

    本源码转载自利快云https://www.lkuaiy.com/ 火山安卓自定义列表框简单操作教程   一.列表项布局设计 1.简单的在布局器中放两个文本框组件并公开. 二.列表数据类设计 1.定义一 ...

  5. 安卓Android ViewModel 超简单实例

    安卓Android ViewModel 超简单实例 文章目录 安卓Android ViewModel 超简单实例 前言 使用步骤 1.引入库 2.继承ViewModel 并定义一个对象 3.到处去用 ...

  6. 安卓 Android之开发简单小应用(一)

    安卓 Android之开发简单小应用(一) 一.简述 记 --没学过Android之开发简单小应用.(课程设计作业) 例子打包:链接:https://pan.baidu.com/s/1LEQ1oWkU ...

  7. 性能超越图神经网络,将标签传递和简单模型结合实现SOTA

    译者 | 刘畅 出品 | AI科技大本营 头图 | CSDN付费下载自视觉中国 图神经网络(GNNs)是图学习中一种主流的技术.然而,对于GNN为什么在实际使用中很成功以及它们是否是优异性能所必需的技 ...

  8. html自定义标签提示,用简单的jquery+CSS创建自定义的a标签title提示tooltip_HTML/Xhtml_网页制作...

    简介 用简单的jquery+CSS创建自定义的a标签title提示,用来代替浏览器默认行为.如图: Javascript代码 代码如下: $(function() { $("a[title] ...

  9. 安卓手机文件管理器简单横向评比 - imsoft.cnblogs

      X-plore文件管理器 个人评价:安卓手机上管理文件的神器,所有文件一览无余,加上自己对软件常用功能的配置,管理文件无比方便.(本人一直使用)   Solid文件管理器 个人评价:用户体验真的很 ...

最新文章

  1. Go 学习笔记(79)— Go 标准库 net(获取本机IP地址)
  2. (原創) 如何將CMOS所擷取的影像傳到PC端? (SOC) (DE2) (TRDB-DC2)
  3. android H5支付 网络环境未能通过安全验证,请稍后再试
  4. 活动目录最佳实践分析器
  5. oracle数据加载控制文件格式,oracle数据加载的几种常用方法
  6. 内向者优势 原版_未来内向的人具有越来越高的竞争力——心理学:学会运用性格优势...
  7. readyState属性和status属性
  8. (230)数据处理或加速方法(加速处理器)
  9. eclipse显示行号和Tab、空格、回车
  10. android shape使用总结
  11. 安卓系统怎么样不Root激活XPOSED框架的方法
  12. Atitit 法学体系树与知识点attilax大总结 法学体系 0301法学类 030101 法学理论 宪法 行政法 民法 商法 婚姻法和继承法 经济法 社会法 刑法 民事诉讼法 行政诉讼法
  13. Java求质数(素数)(超详细)
  14. html调用影音先锋,在网页里嵌入影音先锋web万能播放控件
  15. 查询CI框架的版本号
  16. 安全攻防之BadUsb攻击之CS上线
  17. 多个服务器数据互通_8月6日部分服务器数据互通公告!
  18. 【注意力机制集锦2】BAMSGEDAN原文、结构、源码详解
  19. RequestResponse入门1(Request)
  20. 软件工程复习笔记 类图

热门文章

  1. Unity Shaders and Effects Cookbook (3-6) 创建各向异性高光类型(Anisotropic) 模拟金属拉丝效果
  2. 问题分享-outlook的office365邮箱只能发邮件,收不到邮件
  3. php opp三个特点,opp3.php
  4. 您尝试购买的项目已停止供货
  5. 微信支付提示:当前交易存在被骗风险,为保障你的资金安全,暂时无法完成支付。请谨防刷单兼职、先支付后返利、色情服务等网络骗局。
  6. 4G临近,最新高大上4G旗舰机型推荐
  7. OpenGL超级宝典开发环境配置(VS2010)
  8. 随手记note(第二次团队作业)
  9. Java中的Set接口,了解如何判断是否重复元素。
  10. javax.validation.ValidationException: Unable to create a Configuration