#1 效果图
#2 需求
常见的搜索结果内容展示,大数据内容的标签分类展示,要有点击效果
#3 原理
字标签直接用xml布局添加或add进去,不用具体去计较,同时也使得每个item的扩展性较好;
最外层肯定是一个继承自ViewGroup的容器,我们要重新它的测量onMesure和onLayout方法,通过getChild来计算父控件的宽高和子控件的位置;
计算规则: 1 先在onMesure中获取父控件宽高和模式;
2 测量每个子控件的宽高,并累加后和父控件宽高对比;
3 当高于父控件宽度时,换行(每行View管理在一个List中,而所有行又用一个List管理,最后管理View的数据类型是:List<List<View>>),行高度累加;行宽、行高重新归0;若不高于父控件宽度时,行宽添加,继续遍历
4 注意:由于每个item的高度不一致,每次当前行高与item要做一次比较;父控件的宽要和每行的宽度比较
特殊情况:当刚好最后一个item此时又刚好换行,最后一个item做为一行,行宽高计算到父控件的宽高中去。
#3 自定义ViewGroup的代码
public class FlowLayout extends ViewGroup {
/**
* 用来保存每行views的列表
*/
private List<List<View>> mViewLinesList = new ArrayList<>();
/**
* 用来保存行高的列表
*/
private List<Integer> mLineHeights = new ArrayList<>();
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(),attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 获取父容器为FlowLayout设置的测量模式和大小
int iWidthMode = MeasureSpec.getMode(widthMeasureSpec);
int iHeightMode = MeasureSpec.getMode(heightMeasureSpec);
int iWidthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int iHeightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
int measuredWith = 0;
int measuredHeight = 0;
int iCurLineW = 0;
int iCurLineH = 0;
if(iWidthMode == MeasureSpec.EXACTLY && iHeightMode == MeasureSpec.EXACTLY){
measuredWith = iWidthSpecSize;
measuredHeight = iHeightSpecSize;
}else{
int iChildWidth;
int iChildHeight;
int childCount = getChildCount();
List<View> viewList = new ArrayList<>();
for(int i = 0 ; i < childCount ; i++){
View childView = getChildAt(i);
measureChild(childView, widthMeasureSpec,heightMeasureSpec);
MarginLayoutParams layoutParams = (MarginLayoutParams) childView.getLayoutParams();
iChildWidth = childView.getMeasuredWidth() + layoutParams.leftMargin +
layoutParams.rightMargin;
iChildHeight = childView.getMeasuredHeight() + layoutParams.topMargin +
layoutParams.bottomMargin;
if(iCurLineW + iChildWidth > iWidthSpecSize){
/**1、记录当前行的信息***/
//1、记录当前行的最大宽度,高度累加
measuredWith = Math.max(measuredWith,iCurLineW);
measuredHeight += iCurLineH;
//2、将当前行的viewList添加至总的mViewsList,将行高添加至总的行高List
mViewLinesList.add(viewList);
mLineHeights.add(iCurLineH);
/**2、记录新一行的信息***/
//1、重新赋值新一行的宽、高
iCurLineW = iChildWidth;
iCurLineH = iChildHeight;
// 2、新建一行的viewlist,添加新一行的view
viewList = new ArrayList<View>();
viewList.add(childView);
}else{
// 记录某行内的消息
//1、行内宽度的叠加、高度比较
iCurLineW += iChildWidth;
iCurLineH = Math.max(iCurLineH, iChildHeight);
// 2、添加至当前行的viewList中
viewList.add(childView);
}
/*****3、如果正好是最后一行需要换行**********/
if(i == childCount - 1){
//1、记录当前行的最大宽度,高度累加
measuredWith = Math.max(measuredWith,iCurLineW);
measuredHeight += iCurLineH;
//2、将当前行的viewList添加至总的mViewsList,将行高添加至总的行高List
mViewLinesList.add(viewList);
mLineHeights.add(iCurLineH);
}
}
}
// 最终目的
setMeasuredDimension(measuredWith,measuredHeight);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left,top,right,bottom;
int curTop = 0;
int curLeft = 0;
int lineCount = mViewLinesList.size();
for(int i = 0 ; i < lineCount ; i++) {
List<View> viewList = mViewLinesList.get(i);
int lineViewSize = viewList.size();
for(int j = 0; j < lineViewSize; j++){
View childView = viewList.get(j);
MarginLayoutParams layoutParams = (MarginLayoutParams) childView.getLayoutParams();
left = curLeft + layoutParams.leftMargin;
top = curTop + layoutParams.topMargin;
right = left + childView.getMeasuredWidth();
bottom = top + childView.getMeasuredHeight();
childView.layout(left,top,right,bottom);
curLeft += childView.getMeasuredWidth() + layoutParams.leftMargin
+ layoutParams.rightMargin;
}
curLeft = 0;
curTop += mLineHeights.get(i);
}
mViewLinesList.clear();
mLineHeights.clear();
}
public interface OnItemClickListener{
void onItemClick (View v, int index);
}
public void setOnItemClickListener(final OnItemClickListener listener){
int childCount = getChildCount();
for(int i = 0 ; i < childCount ; i++){
View childView = getChildAt(i);
final int finalI = i;
childView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
listener.onItemClick(v, finalI);
}
});
}
}
}
#4 Demo地址
http://download.csdn.net/detail/baopengjian/9864922