原理:重寫{@link ViewGroup#onMeasure(int, int)}和{@link ViewGroup#onLayout(boolean, int, int, int, int)}方法
public class FlowTagLayout extends ViewGroup {
private static final String TAG = FlowTagLayout.class.getSimpleName();
/**
* FlowLayout not support checked
*/
public static final int FLOW_TAG_CHECKED_NONE = 0;
/**
* FlowLayout support single-select
*/
public static final int FLOW_TAG_CHECKED_SINGLE = 1;
/**
* FlowLayout support multi-select
*/
public static final int FLOW_TAG_CHECKED_MULTI = 2;
/**
* Should be used by subclasses to listen to changes in the dataset
*/
AdapterDataSetObserver mDataSetObserver;
/**
* The adapter containing the data to be displayed by this view
*/
ListAdapter mAdapter;
/**
* the tag click event callback
*/
OnTagClickListener mOnTagClickListener;
/**
* the tag select event callback
*/
OnTagSelectListener mOnTagSelectListener;
/**
* 標籤流式佈局選中模式,預設是不支援選中的
*/
private int mTagCheckMode = FLOW_TAG_CHECKED_NONE;
/**
* 儲存選中的tag
*/
private SparseBooleanArray mCheckedTagArray = new SparseBooleanArray();
public FlowTagLayout(Context context) {
super(context);
}
public FlowTagLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FlowTagLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//獲取Padding
// 獲得它的父容器為它設定的測量模式和大小
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
//FlowLayout最終的寬度和高度值
int resultWidth = 0;
int resultHeight = 0;
//測量時每一行的寬度
int lineWidth = 0;
//測量時每一行的高度,加起來就是FlowLayout的高度
int lineHeight = 0;
//遍歷每個子元素
for (int i = 0, childCount = getChildCount(); i < childCount; i++) {
View childView = getChildAt(i);
//測量每一個子view的寬和高
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
//獲取到測量的寬和高
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
//因為子View可能設定margin,這裡要加上margin的距離
MarginLayoutParams mlp = (MarginLayoutParams) childView.getLayoutParams();
int realChildWidth = childWidth + mlp.leftMargin + mlp.rightMargin;
int realChildHeight = childHeight + mlp.topMargin + mlp.bottomMargin;
//如果當前一行的寬度加上要加入的子view的寬度大於父容器給的寬度,就換行
if ((lineWidth + realChildWidth) > sizeWidth) {
//換行
resultWidth = Math.max(lineWidth, realChildWidth);
resultHeight += realChildHeight;
//換行了,lineWidth和lineHeight重新算
lineWidth = realChildWidth;
lineHeight = realChildHeight;
} else {
//不換行,直接相加
lineWidth += realChildWidth;
//每一行的高度取二者最大值
lineHeight = Math.max(lineHeight, realChildHeight);
}
//遍歷到最後一個的時候,肯定走的是不換行
if (i == childCount - 1) {
resultWidth = Math.max(lineWidth, resultWidth);
resultHeight += lineHeight;
}
setMeasuredDimension(modeWidth == MeasureSpec.EXACTLY ? sizeWidth : resultWidth,
modeHeight == MeasureSpec.EXACTLY ? sizeHeight : resultHeight);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int flowWidth = getWidth();
int childLeft = 0;
int childTop = 0;
//遍歷子控制元件,記錄每個子view的位置
for (int i = 0, childCount = getChildCount(); i < childCount; i++) {
View childView = getChildAt(i);
//跳過View.GONE的子View
if (childView.getVisibility() == View.GONE) {
continue;
}
//獲取到測量的寬和高
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
//因為子View可能設定margin,這裡要加上margin的距離
MarginLayoutParams mlp = (MarginLayoutParams) childView.getLayoutParams();
if (childLeft + mlp.leftMargin + childWidth + mlp.rightMargin > flowWidth) {
//換行處理
childTop += (mlp.topMargin + childHeight + mlp.bottomMargin);
childLeft = 0;
}
//佈局
int left = childLeft + mlp.leftMargin;
int top = childTop + mlp.topMargin;
int right = childLeft + mlp.leftMargin + childWidth;
int bottom = childTop + mlp.topMargin + childHeight;
childView.layout(left, top, right, bottom);
childLeft += (mlp.leftMargin + childWidth + mlp.rightMargin);
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
public ListAdapter getAdapter() {
return mAdapter;
}
class AdapterDataSetObserver extends DataSetObserver {
@Override
public void onChanged() {
super.onChanged();
reloadData();
}
@Override
public void onInvalidated() {
super.onInvalidated();
}
}
/**
* 重新載入重新整理資料
*/
private void reloadData() {
removeAllViews();
boolean isSetted = false;
for (int i = 0; i < mAdapter.getCount(); i++) {
final int j = i;
mCheckedTagArray.put(i, false);
final View childView = mAdapter.getView(i, null, this);
// addView(childView,
// new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));//這個構造方法所然能使用但是編譯器會報錯
addView(childView, new MarginLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)));
if (mAdapter instanceof OnInitSelectedPosition) {
boolean isSelected = ((OnInitSelectedPosition) mAdapter).isSelectedPosition(i);
//判斷一下模式
if (mTagCheckMode == FLOW_TAG_CHECKED_SINGLE) {
//單選只有第一個起作用
if (isSelected && !isSetted) {
mCheckedTagArray.put(i, true);
childView.setSelected(true);
isSetted = true;
}
} else if (mTagCheckMode == FLOW_TAG_CHECKED_MULTI) {
if (isSelected) {
mCheckedTagArray.put(i, true);
childView.setSelected(true);
}
}
}
childView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mTagCheckMode == FLOW_TAG_CHECKED_NONE) {
if (mOnTagClickListener != null) {
mOnTagClickListener.onItemClick(FlowTagLayout.this, childView, j);
}
} else if (mTagCheckMode == FLOW_TAG_CHECKED_SINGLE) {
//判斷狀態
if (mCheckedTagArray.get(j)) {
mCheckedTagArray.put(j, false);
childView.setSelected(false);
if (mOnTagSelectListener != null) {
mOnTagSelectListener.onItemSelect(FlowTagLayout.this, new ArrayList<Integer>(), false);
}
return;
}
for (int k = 0; k < mAdapter.getCount(); k++) {
mCheckedTagArray.put(k, false);
getChildAt(k).setSelected(false);
}
mCheckedTagArray.put(j, true);
childView.setSelected(true);
if (mOnTagSelectListener != null) {
mOnTagSelectListener.onItemSelect(FlowTagLayout.this, Arrays.asList(j), true);
}
} else if (mTagCheckMode == FLOW_TAG_CHECKED_MULTI) {
if (mCheckedTagArray.get(j)) {
mCheckedTagArray.put(j, false);
childView.setSelected(false);
} else {
mCheckedTagArray.put(j, true);
childView.setSelected(true);
}
//回撥
if (mOnTagSelectListener != null) {
List<Integer> list = new ArrayList<Integer>();
for (int k = 0; k < mAdapter.getCount(); k++) {
if (mCheckedTagArray.get(k)) {
list.add(k);
}
}
mOnTagSelectListener.onItemSelect(FlowTagLayout.this, list, true);
}
}
}
});
}
}
/**
* 清除所有被選擇的選項
*/
public void clearAllOption() {
for (int i = 0; i < mAdapter.getCount(); i++) {
if (mCheckedTagArray.get(i)) {
getChildAt(i).setSelected(false);
}
}
}
public void setOnTagClickListener(OnTagClickListener onTagClickListener) {
this.mOnTagClickListener = onTagClickListener;
}
public void setOnTagSelectListener(OnTagSelectListener onTagSelectListener) {
this.mOnTagSelectListener = onTagSelectListener;
}
/**
* 像ListView、GridView一樣使用FlowLayout
*
* @param adapter
*/
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
//清除現有的資料
removeAllViews();
mAdapter = adapter;
if (mAdapter != null) {
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
}
}
/**
* 獲取標籤模式
*
* @return
*/
public int getmTagCheckMode() {
return mTagCheckMode;
}
/**
* 設定標籤選中模式
*
* @param tagMode
*/
public void setTagCheckedMode(int tagMode) {
this.mTagCheckMode = tagMode;
}
}