package com.likebamboo.osa.android.ui.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.HapticFeedbackConstants;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.likebamboo.osa.android.R;
import java.util.ArrayList;
import java.util.List;
/**
* <a href="https://github.com/2dxgujun/AndroidTagGroup">参考https://github.com/2dxgujun/AndroidTagGroup</a>
*
* @author likebamboo
* @version 1.0
* @since 2015-05-18
*/
public class TagGroup extends ViewGroup {
/**
* tag 字体大小
*/
private float mTagTextSize;
/**
* 字体背景
*/
private int mTagBackgroundColor;
/**
* 字体背景
*/
private int mTagBackgroundResId;
/**
* 字体颜色
*/
private int mTagTextColor;
/**
* The horizontal tag spacing,
*/
private int mHorizontalSpacing;
/**
* The vertical tag spacing
*/
private int mVerticalSpacing;
/**
* 标签内边距
*/
private int mHorizontalPadding;
/**
* 标签内边距
*/
private int mVerticalPadding;
/**
* Listener used to dispatch tag click event.
*/
private IOnTagClickListener mOnTagClickListener;
public TagGroup(Context context) {
this(context, null);
}
public TagGroup(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TagGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// Load styled attributes.
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TagGroup, defStyleAttr, R.style.TagGroup);
try {
mTagTextColor = a.getColor(R.styleable.TagGroup_tagTextColor, Color.WHITE);
try {
mTagBackgroundColor = a.getColor(R.styleable.TagGroup_tagBackground, 0);
} catch (Exception e) {
mTagBackgroundResId = a.getResourceId(R.styleable.TagGroup_tagBackground, 0);
}
mTagTextSize = a.getDimensionPixelOffset(R.styleable.TagGroup_tagTextSize, (int) dp2px(14));
mHorizontalSpacing = (int) a.getDimension(R.styleable.TagGroup_horizontalSpacing, dp2px(8));
mVerticalSpacing = (int) a.getDimension(R.styleable.TagGroup_verticalSpacing, dp2px(4));
mHorizontalPadding = (int) a.getDimension(R.styleable.TagGroup_horizontalPadding, dp2px(6));
mVerticalPadding = (int) a.getDimension(R.styleable.TagGroup_verticalPadding, dp2px(1));
} catch (Exception e) {
e.printStackTrace();
} finally {
a.recycle();
}
}
public void setTextSize(float textSize) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
TagView tagView = getTagViewAt(i);
tagView.setTextSize(textSize);
}
requestLayout();
}
/**
* @param resId
*/
public void setTagColor(int resId) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
TagView tagView = getTagViewAt(i);
tagView.setTextColor(resId);
}
}
/**
* @param resId
*/
public void setTagBackgroundResource(int resId) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
TagView tagView = getTagViewAt(i);
tagView.setBackgroundResource(resId);
}
}
/**
* @param color
*/
public void setTagBackgroundColor(int color) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
TagView tagView = getTagViewAt(i);
tagView.setBackgroundColor(color);
}
}
public int getHorizontalSpacing() {
return mHorizontalSpacing;
}
public void setHorizontalSpacing(int horizontalSpacing) {
mHorizontalSpacing = horizontalSpacing;
requestLayout();
}
public int getVerticalSpacing() {
return mVerticalSpacing;
}
public void setVerticalSpacing(int verticalSpacing) {
mVerticalSpacing = verticalSpacing;
requestLayout();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);
int width = 0;
int height = 0;
int row = 0; // The row counter.
int rowWidth = 0; // Calc the current row width.
int rowMaxHeight = 0; // Calc the max tag height, in current row.
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
if (child.getVisibility() != GONE) {
rowWidth += childWidth;
if (rowWidth > widthSize) { // Next line.
rowWidth = childWidth; // The next row width.
height += rowMaxHeight + mVerticalSpacing;
rowMaxHeight = childHeight; // The next row max height.
row++;
} else { // This line.
rowMaxHeight = Math.max(rowMaxHeight, childHeight);
}
rowWidth += mHorizontalSpacing;
}
}
// Account for the last row height.
height += rowMaxHeight;
// Account for the padding too.
height += getPaddingTop() + getPaddingBottom();
// If the tags grouped in one row, set the width to wrap the tags.
if (row == 0) {
width = rowWidth;
width += getPaddingLeft() + getPaddingRight();
} else {// If the tags grouped exceed one line, set the width to match the parent.
width = widthSize;
}
setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : width,
heightMode == MeasureSpec.EXACTLY ? heightSize : height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int parentLeft = getPaddingLeft();
final int parentRight = r - l - getPaddingRight();
final int parentTop = getPaddingTop();
final int parentBottom = b - t - getPaddingBottom();
int childLeft = parentLeft;
int childTop = parentTop;
int rowMaxHeight = 0;
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
if (child.getVisibility() != GONE) {
if (childLeft + width > parentRight) { // Next line
childLeft = parentLeft;
childTop += rowMaxHeight + mVerticalSpacing;
rowMaxHeight = height;
} else {
rowMaxHeight = Math.max(rowMaxHeight, height);
}
child.layout(childLeft, childTop, childLeft + width, childTop + height);
childLeft += width + mHorizontalSpacing;
}
}
}
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.tags = getTags().toArray(new String[]{});
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setTags(ss.tags);
}
/**
* Returns the tags array in group.
*
* @return the tag array
*/
public List<String> getTags() {
final int count = getChildCount();
final List<String> tagList = new ArrayList<>();
for (int i = 0; i < count; i++) {
final TagView tagView = getTagViewAt(i);
tagList.add(tagView.getText().toString());
}
return tagList;
}
/**
* Returns the tag view at the specified position in the group.
*
* @param index the position at which to get the tag view from
* @return the tag view at the specified position or null if the position
* does not exists within this group
*/
protected TagView getTagViewAt(int index) {
return (TagView) getChildAt(index);
}
public void setOnTagClickListener(IOnTagClickListener l) {
mOnTagClickListener = l;
}
/**
* @see #setTags(String...)
*/
public void setTags(List<String> tagList) {
setTags(tagList.toArray(new String[]{}));
}
/**
* Set the tag to this group. It will remove all tags first.
*
* @param tags the tag list to set
*/
public void setTags(String... tags) {
removeAllViews();
for (final String tag : tags) {
if (TextUtils.isEmpty(tag)) {
continue;
}
addTag(tag);
}
}
/**
* 删除标签
*
* @param tag
*/
public void deleteTag(CharSequence tag) {
if (TextUtils.isEmpty(tag)) {
return;
}
int index = getTagIndex(tag);
if (index < getChildCount()) {
removeViewAt(index);
}
}
/**
* 判断是否已经有标签
*
* @param tag
*/
public boolean hasTag(CharSequence tag) {
if (TextUtils.isEmpty(tag)) {
return true;
}
return getTags().contains(tag + "");
}
/**
* 添加标签
*
* @param tag
*/
public void addTag(CharSequence tag) {
addTag(getChildCount(), tag);
}
/**
* 添加标签
*
* @param index
* @param tag the tag to append
*/
public void addTag(int index, CharSequence tag) {
// 如果已经有该标签,将标签提到最前面
if (hasTag(tag)) {
deleteTag(tag);
index = 0;
}
final TagView tagView = new TagView(getContext());
tagView.setText(tag);
tagView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTagTextSize);
tagView.setTextColor(mTagTextColor);
if (mTagBackgroundColor != 0) {
tagView.setBackgroundColor(mTagBackgroundColor);
} else if (mTagBackgroundResId != 0) {
tagView.setBackgroundResource(mTagBackgroundResId);
}
tagView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (view instanceof TagView) {
TagView v = (TagView) view;
if (mOnTagClickListener != null) {
mOnTagClickListener.onTagClick(v.getText().toString().trim());
}
}
}
});
tagView.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
// 长按震动反馈
view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
if (view instanceof TagView) {
TagView v = (TagView) view;
if (mOnTagClickListener != null) {
mOnTagClickListener.onTagLongClick(v.getText().toString().trim());
}
return true;
}
return false;
}
});
// 设置标签内边距
tagView.setPadding(mHorizontalPadding, mVerticalPadding, mHorizontalPadding, mVerticalPadding);
addView(tagView, index, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
}
/**
* 设置tag选中状态
*
* @param tag
* @param selected
*/
public void setTagSelected(String tag, boolean selected) {
int index = getTagIndex(tag);
if (index >= getChildCount()) {
return;
}
TagView tagView = getTagViewAt(index);
if (tagView != null) {
tagView.setSelected(selected);
}
}
/**
* 获取tag选中状态
*
* @param tag
*/
public boolean isTagSelected(String tag) {
int index = getTagIndex(tag);
if (index >= getChildCount()) {
return false;
}
TagView tagView = getTagViewAt(index);
if (tagView != null) {
return tagView.isSelected();
}
return false;
}
/**
* 获取tag 的index
*
* @param tag
* @return
*/
private int getTagIndex(CharSequence tag) {
int index = 0;
for (; index < getChildCount(); index++) {
if (getTagViewAt(index).getText().toString().equals(tag.toString())) {
break;
}
}
return index;
}
private float dp2px(float dp) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
getResources().getDisplayMetrics());
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
public interface IOnTagClickListener {
/**
* @param tag tagName
*/
void onTagClick(String tag);
/**
* tagLongClick
*
* @param tag tagName
*/
void onTagLongClick(String tag);
}
/**
* For {@link TagGroup} save and restore state.
*/
static class SavedState extends BaseSavedState {
int tagCount;
String[] tags;
int checkedPosition;
String input;
public SavedState(Parcel source) {
super(source);
tagCount = source.readInt();
tags = new String[tagCount];
source.readStringArray(tags);
checkedPosition = source.readInt();
input = source.readString();
}
public SavedState(Parcelable superState) {
super(superState);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
tagCount = tags.length;
dest.writeInt(tagCount);
dest.writeStringArray(tags);
dest.writeInt(checkedPosition);
dest.writeString(input);
}
public static final Creator<SavedState> CREATOR =
new Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
/**
* The tag view
*/
class TagView extends TextView {
public TagView(Context context) {
super(context);
}
public TagView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TagView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
}