您的位置:首页 > 移动开发 > Android开发

android自定义控件-瀑布流

2016-05-04 11:41 531 查看
业务需要,需要写一个支持自动换行的容器。

之前同事写的感觉可定制化程度太低,于是自己写了一个。。

支持的效果如下图

备注:所有容器添加的都是一样的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<ArrayList<View>> mViews = new ArrayList<ArrayList<View>>();

// 基础属性-最大宽度
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<View> line0 = new ArrayList<View>();
mViews.add(line0);

List<View> prepareViews = new ArrayList<View>();
HashMap<View, int[]> whMap = new HashMap<View, int[]>();
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<View> line1 = new ArrayList<View>();
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<View> 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<View>();
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<View> 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: