前言:可以設定标簽的選中效果。 可以設定标簽的選中類型:不可選中、單選、限數量多選和不限數量多選等, 并支援設定必選項等功能
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資料的key
private static final int KEY_DATA = R.id.labels_data;
//用于儲存label位置的key
private 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();
}
}
@Override
protected 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;
}
@Override
protected 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";
@Override
protected 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;
}
@Override
protected 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>() {
@Override
public 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));
}
@Override
public 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 = {"咖啡", "飲食", "男裝", "女裝", "眼鏡", "内衣配飾", "母嬰",
"鞋靴", "運動", "箱包"};
@Override
protected 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類型等
//标題和id
listData.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>() {
@Override
public CharSequence getLabelText(TextView label, int position, LabelsDto data) {
return data.getLabelTitle();根據data和position傳回label需要顯示的資料。
}
});
//第二步:點選事件
labelsView.setOnLabelClickListener(new LabelsView.OnLabelClickListener() {
@Override
public 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() {
@Override
public void onLabelSelectChange(TextView label, Object data, boolean isSelect, int position) {
//label是被選中的标簽,data是标簽所對應的資料,isSelect是是否選中,position是标簽的位置
}
});
}
@Override
public 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;//标簽id
public 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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/button1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="不可選中" />
<Button
android:id="@+id/button2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="單選,可以反選" />
<Button
android:id="@+id/button3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="單選,不可以反選" />
<Button
android:id="@+id/button4"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="多選" />
</LinearLayout>
<word.hello.com.module.view.LabelsView
android: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" />
<TextView
android: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