android自定义控件-瀑布流
2016-05-04 11:41
531 查看
业务需要,需要写一个支持自动换行的容器。
之前同事写的感觉可定制化程度太低,于是自己写了一个。。
支持的效果如下图
备注:所有容器添加的都是一样的views,只是设置的参数不一样。
代码如下:
工程如下:
http://download.csdn.net/detail/aa5279aa/9509705
如需转载,请标明出处:http://write.blog.csdn.net/postedit/51313712
之前同事写的感觉可定制化程度太低,于是自己写了一个。。
支持的效果如下图
备注:所有容器添加的都是一样的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
相关文章推荐
- Android文件的读写详解
- Android中Parcel的分析以及使用
- Material Design之TextInputLayout
- Android的Usb设备的监听(Dev)外设端口的判定以及耳机的插拔
- uboot流程分析--修改android启动模式按键
- class.isAssignableFrom
- Android第三方acharrtengine绘制折线图
- Android Studio:Multiple dex files define Landroid/support/annotation/AnimRes
- android不将apk包编译到系统里的方法
- android不将apk包编译到系统里的方法
- android不将apk包编译到系统里的方法
- android不将apk包编译到系统里的方法
- Android Studio上方便使用butterknife注解框架的偷懒插件Android Butterknife Zelezny
- android 图片加载和缓存开源项目 Picasso
- Android 颜色渲染(九) PorterDuff及Xfermode解析
- Android客户端发送Get和Post请求
- 圆形头像带描边的工具类
- android安装本地应用,并提示打开还是完成安装
- Android如何生成公共属性的get,set方法时,去除 成员变量的m前缀
- Android中的Uri