安卓标签LabelsView的简单使用
前言:可以设置标签的选中效果。 可以设置标签的选中类型:不可选中、单选、限数量多选和不限数量多选等, 并支持设置必选项等功能
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的简单使用相关推荐
- JSP自定义标签就是如此简单
2019独角兽企业重金招聘Python工程师标准>>> 为什么要用到简单标签? 上一篇博客中我已经讲解了传统标签,想要开发自定义标签,大多数情况下都要重写doStartTag(),d ...
- 一图秒懂 - MD文件语法标签,最简单的markdown入门
代码-效果图 https://rudon.blog.csdn.net/article/details/104272292 (完整代码在本文最下面) 实时查看MD文件效果 - 在线Markdown预览 ...
- android 模拟器作用,安卓模拟器:看似简单实则用处极大的日常功能
原标题:安卓模拟器:看似简单实则用处极大的日常功能 逍遥安卓模拟器每个版本都会推出一些实用性比较强大的功能,并不断完善现有功能,为用户带来良好的游戏体验,今天给大家带来的是每天使用率较高的功能完整版解 ...
- 火山安卓自定义列表框简单操作教程
本源码转载自利快云https://www.lkuaiy.com/ 火山安卓自定义列表框简单操作教程 一.列表项布局设计 1.简单的在布局器中放两个文本框组件并公开. 二.列表数据类设计 1.定义一 ...
- 安卓Android ViewModel 超简单实例
安卓Android ViewModel 超简单实例 文章目录 安卓Android ViewModel 超简单实例 前言 使用步骤 1.引入库 2.继承ViewModel 并定义一个对象 3.到处去用 ...
- 安卓 Android之开发简单小应用(一)
安卓 Android之开发简单小应用(一) 一.简述 记 --没学过Android之开发简单小应用.(课程设计作业) 例子打包:链接:https://pan.baidu.com/s/1LEQ1oWkU ...
- 性能超越图神经网络,将标签传递和简单模型结合实现SOTA
译者 | 刘畅 出品 | AI科技大本营 头图 | CSDN付费下载自视觉中国 图神经网络(GNNs)是图学习中一种主流的技术.然而,对于GNN为什么在实际使用中很成功以及它们是否是优异性能所必需的技 ...
- html自定义标签提示,用简单的jquery+CSS创建自定义的a标签title提示tooltip_HTML/Xhtml_网页制作...
简介 用简单的jquery+CSS创建自定义的a标签title提示,用来代替浏览器默认行为.如图: Javascript代码 代码如下: $(function() { $("a[title] ...
- 安卓手机文件管理器简单横向评比 - imsoft.cnblogs
X-plore文件管理器 个人评价:安卓手机上管理文件的神器,所有文件一览无余,加上自己对软件常用功能的配置,管理文件无比方便.(本人一直使用) Solid文件管理器 个人评价:用户体验真的很 ...
最新文章
- Go 学习笔记(79)— Go 标准库 net(获取本机IP地址)
- (原創) 如何將CMOS所擷取的影像傳到PC端? (SOC) (DE2) (TRDB-DC2)
- android H5支付 网络环境未能通过安全验证,请稍后再试
- 活动目录最佳实践分析器
- oracle数据加载控制文件格式,oracle数据加载的几种常用方法
- 内向者优势 原版_未来内向的人具有越来越高的竞争力——心理学:学会运用性格优势...
- readyState属性和status属性
- (230)数据处理或加速方法(加速处理器)
- eclipse显示行号和Tab、空格、回车
- android shape使用总结
- 安卓系统怎么样不Root激活XPOSED框架的方法
- Atitit 法学体系树与知识点attilax大总结 法学体系		 0301法学类	030101 法学理论	宪法 行政法 民法 商法 婚姻法和继承法 经济法 社会法 刑法 民事诉讼法 行政诉讼法
- Java求质数(素数)(超详细)
- html调用影音先锋,在网页里嵌入影音先锋web万能播放控件
- 查询CI框架的版本号
- 安全攻防之BadUsb攻击之CS上线
- 多个服务器数据互通_8月6日部分服务器数据互通公告!
- 【注意力机制集锦2】BAMSGEDAN原文、结构、源码详解
- RequestResponse入门1(Request)
- 软件工程复习笔记 类图
热门文章
- Unity Shaders and Effects Cookbook (3-6) 创建各向异性高光类型(Anisotropic) 模拟金属拉丝效果
- 问题分享-outlook的office365邮箱只能发邮件,收不到邮件
- php opp三个特点,opp3.php
- 您尝试购买的项目已停止供货
- 微信支付提示:当前交易存在被骗风险,为保障你的资金安全,暂时无法完成支付。请谨防刷单兼职、先支付后返利、色情服务等网络骗局。
- 4G临近,最新高大上4G旗舰机型推荐
- OpenGL超级宝典开发环境配置(VS2010)
- 随手记note(第二次团队作业)
- Java中的Set接口,了解如何判断是否重复元素。
- javax.validation.ValidationException: Unable to create a Configuration