业务需要,需要写一个支持自动换行的容器。
之前同事写的感觉可定制化程度太低,于是自己写了一个。。
支持的效果如下图
备注:所有容器添加的都是一样的views,只是设置的参数不一样。
代码如下:
package com.xt.androidproject.mywidget;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import com.xt.androidproject.util.DeviceInfoUtil;
/**
* Created by lxl on 2015/12/15;
* Update by lxl on 2016/04/29.
*/
public class HotelTagsView extends ViewGroup {
// 默认间距
/**
* 水平方向间隔
*/
private int mHorizontalSpacing = DeviceInfoUtil.getPixelFromDip(
this.getContext(), 3);
/**
* 竖直方向间隔
*/
private int mVerticalSpacing = DeviceInfoUtil.getPixelFromDip(
this.getContext(), 5);
/**
* 记录要展示的view
*/
private List> mViews = new ArrayList>();
// 基础属性-最大宽度
private int mMaxWidth = 0;
/**
* 最后一个view是控制不显示(true),还是显示一部分(false)
*/
private boolean mIsNeedGrep = true;
/**
* 是否可换行 不可换行其实就是把最大行数设置为1
*/
private boolean mIsCanMulti = false;
/**
* 必定显示的view,当一行显示不下时,该view在第二行展示. mCanWrap属性设置为true的时候,该属性无效
*/
private View mMainView;
/**
* 最大行数
*/
private int mMaxLine = 3;//
/**
* 是否自适应宽度
*/
private boolean mWarpWidth = false;//
public HotelTagsView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mViews.clear();
int maxShowWidth = mMaxWidth;
if (maxShowWidth <= 0) {
maxShowWidth = MeasureSpec.getSize(widthMeasureSpec);
}
// int maxShowHeight =
// MeasureSpec.getSize(heightMeasureSpec);//高度还是不要设置了
/** current line */
int N = this.getChildCount();
if (N == 0) {
return;
}
int maxWidth = 0;
int maxHeight = 0;
/** each padding value */
int paddingTop = this.getPaddingTop();
int paddingBottom = this.getPaddingBottom();
int paddingLeft = this.getPaddingLeft();
int paddingRight = this.getPaddingRight();
int[] params;
if (mMainView == null) {
params = measureMultiLine(N, maxShowWidth, paddingLeft,
paddingRight);
maxWidth += (paddingRight + paddingLeft);
maxHeight += (paddingTop + paddingBottom);
} else {
params = measureHaveMainViewLine(N, maxShowWidth, paddingLeft,
paddingRight);
maxWidth += (paddingRight + paddingLeft);
maxHeight += (paddingTop + paddingBottom);
}
// 设置view宽和高
maxWidth = mWarpWidth ? params[0] : maxShowWidth;
maxHeight = params[1];
setMeasuredDimension(maxWidth, maxHeight);
}
/**
* 指定了mMainView,会返回两行
*/
private int[] measureHaveMainViewLine(int N, int maxShowWidth,
int paddingLeft, int paddingRight) {
ArrayList line0 = new ArrayList();
mViews.add(line0);
List prepareViews = new ArrayList();
HashMap whMap = new HashMap();
int aWidth = 0;
int pWidth = 0;
int maxWidth = 0;
int maxHeight = 0;
int horizontalWidth = 0;
// 遍历分类普通和预存views
for (int i = 0; i < N; i++) {
View childView = this.getChildAt(i);
measureChildView(childView);
int measuredWidth = childView.getMeasuredWidth();
int measuredHeight = childView.getMeasuredHeight();
if (i > 0) {
horizontalWidth = mHorizontalSpacing;
}
if (childView == mMainView || prepareViews.size() > 0) {
prepareViews.add(childView);
pWidth += (measuredWidth + horizontalWidth);
} else {
line0.add(childView);
aWidth += (measuredWidth + horizontalWidth);
}
int[] wh = new int[2];
wh[0] = measuredWidth;
wh[1] = measuredHeight;
whMap.put(childView, wh);
}
if ((paddingLeft + paddingRight + aWidth + pWidth) <= maxShowWidth) {
// 可以完全展示,一行展示
line0.addAll(prepareViews);
} else if (prepareViews.size() > 0) {
// 不能完全展示,mainView二行展示
View remove = prepareViews.remove(0);
int[] removewh = whMap.get(remove);
maxHeight += remove.getMeasuredHeight() + mVerticalSpacing;
maxWidth = remove.getMeasuredWidth();
maxWidth = removewh[0];
ArrayList line1 = new ArrayList();
line1.add(remove);
mViews.add(line1);
for (View view : prepareViews) {
int[] wh = whMap.get(view);
if ((aWidth + wh[0]) <= maxShowWidth) {
// 判断是否为第一个
aWidth += (wh[0] + (line0.size() > 0 ? horizontalWidth : 0));
line0.add(view);
} else {
// 根据标记位判断是否添加最后一个
if (!mIsNeedGrep) {
line0.add(view);
}
break;
}
}
}
/** 宽度为第一行的宽度 */
int realWidth = 0;
int realHeight = 0;
if (line0.size() > 0) {
int INDEX = line0.size() - 1;
for (int index = 0; index <= INDEX; index++) {
realWidth += line0.get(index).getMeasuredWidth()
+ mHorizontalSpacing;
realHeight = Math.max(realHeight, line0.get(index)
.getMeasuredHeight());
}
}
maxHeight += realHeight;
maxWidth = Math.max(maxWidth, realWidth);
return new int[] { maxWidth, maxHeight };
}
private int[] measureMultiLine(int N, int maxShowWidth, int paddingLeft,
int paddingRight) {
int[] maxParams = new int[2];
int currentLine = 0;
int lineWidth = 0;
int lineHeight = 0;
int verticalHeight = 0;
int horizontalWidth = 0;
boolean isFirst = true;
for (int i = 0; i < N; i++) {
View childView = this.getChildAt(i);
measureChildView(childView);
int measuredWidth = childView.getMeasuredWidth();
int measuredHeight = childView.getMeasuredHeight();
// 判断是否需要新建一行
ArrayList arrayList;
if ((maxShowWidth - lineWidth - paddingRight - paddingLeft) >= measuredWidth
+ horizontalWidth) {
lineHeight = Math.max(lineHeight, measuredHeight);
lineWidth += (measuredWidth + horizontalWidth);
// 每行的第一个宽度间距为0,第二个起计算宽度间距
horizontalWidth = mHorizontalSpacing;
} else if (!mIsNeedGrep && isFirst) {
isFirst = false;
lineHeight = Math.max(lineHeight, measuredHeight);
lineWidth += (measuredWidth + horizontalWidth);
horizontalWidth = mHorizontalSpacing;
} else {
isFirst = true;
maxParams[0] = maxParams[0] > lineWidth ? maxParams[0]
: lineWidth;
maxParams[1] += (lineHeight + verticalHeight);
verticalHeight = mVerticalSpacing;// 第二行开始每行增加间距
if (++currentLine >= mMaxLine) {
return maxParams;
}
lineWidth = measuredWidth;
lineHeight = measuredHeight;
}
if (mViews.size() > currentLine) {
arrayList = mViews.get(currentLine);
} else {
arrayList = new ArrayList();
mViews.add(arrayList);
}
arrayList.add(childView);
}
maxParams[0] = maxParams[0] > lineWidth ? maxParams[0] : lineWidth;
maxParams[1] += (lineHeight + verticalHeight);// 加上最后一行的最大高度
return maxParams;
}
private void measureChildView(View childView) {
int cwMeasureSpec = MeasureSpec.UNSPECIFIED;
int chMeasureSpec = MeasureSpec.UNSPECIFIED;
ViewGroup.LayoutParams lp = childView.getLayoutParams();
if (lp.width >= 0) {
cwMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width,
MeasureSpec.AT_MOST);
}
if (lp.height >= 0) {
chMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
MeasureSpec.AT_MOST);
}
childView.measure(cwMeasureSpec, chMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left = getPaddingLeft();
int top = getPaddingTop();
int lineHeight = 0;
int startHeight = top;
for (ArrayList list : mViews) {
for (View view : list) {
int viewLeftMargin = 0;
if (view.getLayoutParams() instanceof LinearLayout.LayoutParams) {
LinearLayout.LayoutParams viewLayoutParams = (LinearLayout.LayoutParams) view
.getLayoutParams();
viewLeftMargin = viewLayoutParams.leftMargin;
}
int measuredHeight = view.getMeasuredHeight();
int measuredWidth = view.getMeasuredWidth();
if (viewLeftMargin < 0) {
view.layout(viewLeftMargin + left - mHorizontalSpacing,
startHeight, viewLeftMargin + left + measuredWidth,
startHeight + measuredHeight);
} else {
view.layout(left, startHeight, left + measuredWidth,
startHeight + measuredHeight);
}
left += (view.getMeasuredWidth() + mHorizontalSpacing);
lineHeight = Math.max(measuredHeight, lineHeight);
}
startHeight = startHeight + lineHeight + mVerticalSpacing;
left = getPaddingLeft();
}
}
// 以下方法全部使用init来设置属性,避免set,build方法和继承的方法的方法名相似,不好找
public HotelTagsView initHorizontalSpacing(int horizontalSpacing) {
this.mHorizontalSpacing = horizontalSpacing;
return this;
}
/**
* 设置标签显示的最大宽度
*
* @param maxWidth
*/
public HotelTagsView initMaxWidth(int maxWidth) {
this.mMaxWidth = maxWidth;
return this;
}
/**
* 每行最后一个展示不全的标签是否显示出来
*
* @param isNeedGrep
*/
public HotelTagsView initIsNeedGrep(boolean isNeedGrep) {
this.mIsNeedGrep = isNeedGrep;
return this;
}
/**
* 是否支持多行
*
* @param isCanMulti
* @return
*/
public HotelTagsView initIsCanMulti(boolean isCanMulti) {
this.mIsCanMulti = isCanMulti;
if (!mIsCanMulti) {
mMaxLine = 1;
}
return this;
}
/**
* mainView如果显示不开,则第二行显示
*
* @param view
*/
public HotelTagsView initMainTagView(View view) {
this.mMainView = view;
return this;
}
/**
* 最大行数
*
* @param maxLine
* @return
*/
public HotelTagsView initMaxLine(int maxLine) {
this.mMaxLine = maxLine;
return this;
}
/**
* 宽度自适应
*
* @return
*/
public HotelTagsView initWarpWidth(boolean warpWidth) {
this.mWarpWidth = warpWidth;
return this;
}
}
http://download.csdn.net/detail/aa5279aa/9509705
如需转载,请标明出处:http://write.blog.csdn.net/postedit/51313712