标签控件,可选择
public class TagFlowLayout extends FlowLayout
implements TagAdapter.OnDataChangedListener {
private TagAdapter mTagAdapter;
private int mSelectedMax = -1;//-1为不限制数量
private static final String TAG = "TagFlowLayout";
private Set<Integer> mSelectedView = new HashSet<Integer>();
private OnSelectListener mOnSelectListener;
private OnTagClickListener mOnTagClickListener;
public interface OnSelectListener {
void onSelected(Set<Integer> selectPosSet);
}
public interface OnTagClickListener {
boolean onTagClick(View view, int position, FlowLayout parent);
}
public TagFlowLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TagFlowLayout);
mSelectedMax = ta.getInt(R.styleable.TagFlowLayout_max_select, -1);
ta.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int cCount = getChildCount();
for (int i = 0; i < cCount; i++) {
TagView tagView = (TagView) getChildAt(i);
if (tagView.getVisibility() == View.GONE) {
continue;
}
if (tagView.getTagView().getVisibility() == View.GONE) {
tagView.setVisibility(View.GONE);
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public void setOnSelectListener(OnSelectListener onSelectListener) {
mOnSelectListener = onSelectListener;
}
public void setOnTagClickListener(OnTagClickListener onTagClickListener) {
mOnTagClickListener = onTagClickListener;
}
public void setAdapter(TagAdapter adapter) {
mTagAdapter = adapter;
mTagAdapter.setOnDataChangedListener(this);
mSelectedView.clear();
changeAdapter();
}
@SuppressWarnings("ResourceType")
private void changeAdapter() {
removeAllViews();
TagAdapter adapter = mTagAdapter;
TagView tagViewContainer = null;
HashSet preCheckedList = mTagAdapter.getPreCheckedList();
for (int i = 0; i < adapter.getCount(); i++) {
View tagView = adapter.getView(this, i, adapter.getItem(i));
tagViewContainer = new TagView(getContext());
tagView.setDuplicateParentStateEnabled(true);
if (tagView.getLayoutParams() != null) {
tagViewContainer.setLayoutParams(tagView.getLayoutParams());
} else {
MarginLayoutParams lp = new MarginLayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
lp.setMargins(dip2px(getContext(), 5),
dip2px(getContext(), 5),
dip2px(getContext(), 5),
dip2px(getContext(), 5));
tagViewContainer.setLayoutParams(lp);
}
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
tagView.setLayoutParams(lp);
tagViewContainer.addView(tagView);
addView(tagViewContainer);
if (preCheckedList.contains(i)) {
setChildChecked(i, tagViewContainer);
}
if (mTagAdapter.setSelected(i, adapter.getItem(i))) {
setChildChecked(i, tagViewContainer);
}
tagView.setClickable(false);
final TagView finalTagViewContainer = tagViewContainer;
final int position = i;
tagViewContainer.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
doSelect(finalTagViewContainer, position);
if (mOnTagClickListener != null) {
mOnTagClickListener.onTagClick(finalTagViewContainer, position,
TagFlowLayout.this);
}
}
});
}
mSelectedView.addAll(preCheckedList);
}
public void setMaxSelectCount(int count) {
if (mSelectedView.size() > count) {
Log.w(TAG, "you has already select more than " + count + " views , so it will be clear .");
mSelectedView.clear();
}
mSelectedMax = count;
}
public Set<Integer> getSelectedList() {
return new HashSet<Integer>(mSelectedView);
}
private void setChildChecked(int position, TagView view) {
view.setChecked(true);
mTagAdapter.onSelected(position, view.getTagView());
}
private void setChildUnChecked(int position, TagView view) {
view.setChecked(false);
mTagAdapter.unSelected(position, view.getTagView());
}
private void doSelect(TagView child, int position) {
if (!child.isChecked()) {
//处理max_select=1的情况
if (mSelectedMax == 1 && mSelectedView.size() == 1) {
Iterator<Integer> iterator = mSelectedView.iterator();
Integer preIndex = iterator.next();
TagView pre = (TagView) getChildAt(preIndex);
setChildUnChecked(preIndex, pre);
setChildChecked(position, child);
mSelectedView.remove(preIndex);
mSelectedView.add(position);
} else {
if (mSelectedMax > 0 && mSelectedView.size() >= mSelectedMax) {
return;
}
setChildChecked(position, child);
mSelectedView.add(position);
}
} else {
setChildUnChecked(position, child);
mSelectedView.remove(position);
}
if (mOnSelectListener != null) {
mOnSelectListener.onSelected(new HashSet<Integer>(mSelectedView));
}
}
public TagAdapter getAdapter() {
return mTagAdapter;
}
private static final String KEY_CHOOSE_POS = "key_choose_pos";
private static final String KEY_DEFAULT = "key_default";
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable(KEY_DEFAULT, super.onSaveInstanceState());
String selectPos = "";
if (mSelectedView.size() > 0) {
for (int key : mSelectedView) {
selectPos += key + "|";
}
selectPos = selectPos.substring(0, selectPos.length() - 1);
}
bundle.putString(KEY_CHOOSE_POS, selectPos);
return bundle;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
String mSelectPos = bundle.getString(KEY_CHOOSE_POS);
if (!TextUtils.isEmpty(mSelectPos)) {
String[] split = mSelectPos.split("\|");
for (String pos : split) {
int index = Integer.parseInt(pos);
mSelectedView.add(index);
TagView tagView = (TagView) getChildAt(index);
if (tagView != null) {
setChildChecked(index, tagView);
}
}
}
super.onRestoreInstanceState(bundle.getParcelable(KEY_DEFAULT));
return;
}
super.onRestoreInstanceState(state);
}
@Override
public void onChanged() {
mSelectedView.clear();
changeAdapter();
}
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
}
基类流式布局
public class FlowLayout extends ViewGroup {
private static final String TAG = "FlowLayout";
private static final int LEFT = -1;
private static final int CENTER = 0;
private static final int RIGHT = 1;
protected List<List<View>> mAllViews = new ArrayList<List<View>>();
protected List<Integer> mLineHeight = new ArrayList<Integer>();
protected List<Integer> mLineWidth = new ArrayList<Integer>();
private int mGravity;
private List<View> lineViews = new ArrayList<>();
public FlowLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TagFlowLayout);
mGravity = ta.getInt(R.styleable.TagFlowLayout_tag_gravity, LEFT);
int layoutDirection = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault());
if (layoutDirection == LayoutDirection.RTL) {
if (mGravity == LEFT) {
mGravity = RIGHT;
} else {
mGravity = LEFT;
}
}
ta.recycle();
}
public FlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FlowLayout(Context context) {
this(context, null);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
// wrap_content
int width = 0;
int height = 0;
int lineWidth = 0;
int lineHeight = 0;
int cCount = getChildCount();
for (int i = 0; i < cCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() == View.GONE) {
if (i == cCount - 1) {
width = Math.max(lineWidth, width);
height += lineHeight;
}
continue;
}
measureChild(child, widthMeasureSpec, heightMeasureSpec);
MarginLayoutParams lp = (MarginLayoutParams) child
.getLayoutParams();
int childWidth = child.getMeasuredWidth() + lp.leftMargin
+ lp.rightMargin;
int childHeight = child.getMeasuredHeight() + lp.topMargin
+ lp.bottomMargin;
if (lineWidth + childWidth > sizeWidth - getPaddingLeft() - getPaddingRight()) {
width = Math.max(width, lineWidth);
lineWidth = childWidth;
height += lineHeight;
lineHeight = childHeight;
} else {
lineWidth += childWidth;
lineHeight = Math.max(lineHeight, childHeight);
}
if (i == cCount - 1) {
width = Math.max(lineWidth, width);
height += lineHeight;
}
}
setMeasuredDimension(
//
modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width + getPaddingLeft() + getPaddingRight(),
modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height + getPaddingTop() + getPaddingBottom()//
);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mAllViews.clear();
mLineHeight.clear();
mLineWidth.clear();
lineViews.clear();
int width = getWidth();
int lineWidth = 0;
int lineHeight = 0;
int cCount = getChildCount();
for (int i = 0; i < cCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() == View.GONE) continue;
MarginLayoutParams lp = (MarginLayoutParams) child
.getLayoutParams();
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
if (childWidth + lineWidth + lp.leftMargin + lp.rightMargin > width - getPaddingLeft() - getPaddingRight()) {
mLineHeight.add(lineHeight);
mAllViews.add(lineViews);
mLineWidth.add(lineWidth);
lineWidth = 0;
lineHeight = childHeight + lp.topMargin + lp.bottomMargin;
lineViews = new ArrayList<View>();
}
lineWidth += childWidth + lp.leftMargin + lp.rightMargin;
lineHeight = Math.max(lineHeight, childHeight + lp.topMargin
+ lp.bottomMargin);
lineViews.add(child);
}
mLineHeight.add(lineHeight);
mLineWidth.add(lineWidth);
mAllViews.add(lineViews);
int left = getPaddingLeft();
int top = getPaddingTop();
int lineNum = mAllViews.size();
for (int i = 0; i < lineNum; i++) {
lineViews = mAllViews.get(i);
lineHeight = mLineHeight.get(i);
// set gravity
int currentLineWidth = this.mLineWidth.get(i);
switch (this.mGravity) {
case LEFT:
left = getPaddingLeft();
break;
case CENTER:
left = (width - currentLineWidth) / 2 + getPaddingLeft();
break;
case RIGHT:
// 适配了rtl,需要补偿一个padding值
left = width - (currentLineWidth + getPaddingLeft()) - getPaddingRight();
// 适配了rtl,需要把lineViews里面的数组倒序排
Collections.reverse(lineViews);
break;
}
for (int j = 0; j < lineViews.size(); j++) {
View child = lineViews.get(j);
if (child.getVisibility() == View.GONE) {
continue;
}
MarginLayoutParams lp = (MarginLayoutParams) child
.getLayoutParams();
int lc = left + lp.leftMargin;
int tc = top + lp.topMargin;
int rc = lc + child.getMeasuredWidth();
int bc = tc + child.getMeasuredHeight();
child.layout(lc, tc, rc, bc);
left += child.getMeasuredWidth() + lp.leftMargin
+ lp.rightMargin;
}
top += lineHeight;
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {
return new MarginLayoutParams(p);
}
}
adapter基类
package com.example.myapplication.data;
import android.util.Log;
import android.view.View;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public abstract class TagAdapter<T> {
private List<T> mTagDatas;
private OnDataChangedListener mOnDataChangedListener;
@Deprecated
private HashSet<Integer> mCheckedPosList = new HashSet<Integer>();
public TagAdapter(List<T> datas) {
mTagDatas = datas;
}
@Deprecated
public TagAdapter(T[] datas) {
mTagDatas = new ArrayList<T>(Arrays.asList(datas));
}
interface OnDataChangedListener {
void onChanged();
}
void setOnDataChangedListener(OnDataChangedListener listener) {
mOnDataChangedListener = listener;
}
@Deprecated
public void setSelectedList(int... poses) {
Set<Integer> set = new HashSet<>();
for (int pos : poses) {
set.add(pos);
}
setSelectedList(set);
}
@Deprecated
public void setSelectedList(Set<Integer> set) {
mCheckedPosList.clear();
if (set != null) {
mCheckedPosList.addAll(set);
}
notifyDataChanged();
}
@Deprecated
HashSet<Integer> getPreCheckedList() {
return mCheckedPosList;
}
public int getCount() {
return mTagDatas == null ? 0 : mTagDatas.size();
}
public void notifyDataChanged() {
if (mOnDataChangedListener != null)
mOnDataChangedListener.onChanged();
}
public T getItem(int position) {
return mTagDatas.get(position);
}
public abstract View getView(FlowLayout parent, int position, T t);
public void onSelected(int position, View view){
}
public void unSelected(int position, View view){
}
public boolean setSelected(int position, T t) {
return false;
}
}
TagView 类
package com.example.myapplication.data;
import android.content.Context;
import android.view.View;
import android.widget.Checkable;
import android.widget.FrameLayout;
/**
* Created by zhy on 15/9/10.
*/
public class TagView extends FrameLayout implements Checkable
{
private boolean isChecked;
private static final int[] CHECK_STATE = new int[]{android.R.attr.state_checked};
public TagView(Context context)
{
super(context);
}
public View getTagView()
{
return getChildAt(0);
}
@Override
public int[] onCreateDrawableState(int extraSpace)
{
int[] states = super.onCreateDrawableState(extraSpace + 1);
if (isChecked())
{
mergeDrawableStates(states, CHECK_STATE);
}
return states;
}
@Override
public void setChecked(boolean checked)
{
if (this.isChecked != checked)
{
this.isChecked = checked;
refreshDrawableState();
}
}
@Override
public boolean isChecked()
{
return isChecked;
}
@Override
public void toggle()
{
setChecked(!isChecked);
}
}
attrs.xml
<declare-styleable name="TagFlowLayout">
<attr name="max_select" format="integer"></attr>
<attr name="tag_gravity">
<enum name="left" value="-1" />
<enum name="center" value="0" />
<enum name="right" value="1" />
</attr>
</declare-styleable>
应用层
public class FActivity extends Activity {
private String[] mVals = new String[]
{"Hello", "Android", "Weclome Hi ", "Button", "TextView", "Hello",
"Android", "Weclome", "Button ImageView", "TextView", "Helloworld",
"Android", "Weclome Hello", "Button Text", "TextView","Hello", "Android", "Weclome Hi ", "Button", "TextView", "Hello",
"Android", "Weclome", "Button ImageView"};
private TagFlowLayout mFlowLayout;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.flayout);
mFlowLayout = (TagFlowLayout) findViewById(R.id.tagflow);
mFlowLayout.setMaxSelectCount(2);
mFlowLayout.setAdapter(new TagAdapter<String>(mVals)
{
@Override
public View getView(FlowLayout parent, int position, String s)
{
TextView tv = (TextView) LayoutInflater.from(FActivity.this).inflate(R.layout.tv,null);
// TextView tx=tv.findViewById(R.id.text);
tv.setText(s);
return tv;
}
});
// mFlowLayout.setOnSelectListener(new TagFlowLayout.OnSelectListener() {
// @Override
// public void onSelected(Set<Integer> selectPosSet) {
//// if()
// }
// });
// mFlowLayout.setOnTagClickListener(new TagFlowLayout.OnTagClickListener() {
// @Override
// public boolean onTagClick(View view, int position, FlowLayout parent) {
//
// return false;
// }
// });
}
}
```
```