自定义可设置每行最多显示数目,每个子View宽度等分的FlowView
来源:互联网 发布:18u网络机柜 编辑:程序博客网 时间:2024/06/06 02:24
// 存储所有子View private List<List<View>> mAllChildViews = new ArrayList<List<View>>(); // 每一行的高度 private List<Integer> mLineHeight = new ArrayList<Integer>(); // 最多的列数 默认设置为4,可以自己另行设置 private int lineMaxColumn = 4; public FlowView(Context context) { this(context, null); } public FlowView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public FlowView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); }
设置最大的列数,最大列数的取值范围为大于1
public void setLineMaxColumn(int count) { if (count < 1) { this.lineMaxColumn = 1; } else { this.lineMaxColumn = count; } }
首先得到其父容器传入的测量模式和宽高的计算值,然后遍历所有的childView,使用measureChild方法对所有的childView进行测量。然后根据所有childView的测量得出的宽和高得到该ViewGroup如果设置为wrap_content时的宽和高。最后根据模式,如果是MeasureSpec.EXACTLY则直接使用父ViewGroup传入的宽和高,否则设置为自己计算的宽和高。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 父控件传进来的宽度和高度以及对应的测量模式 int sizeWidth = MeasureSpec.getSize(widthMeasureSpec); int modeWidth = MeasureSpec.getMode(widthMeasureSpec); int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); int modeHeight = MeasureSpec.getMode(heightMeasureSpec); // 如果当前ViewGroup的宽高为wrap_content的情况 int width = getPaddingLeft() + getPaddingRight();// 自己测量的 宽度 int height = getPaddingTop() + getPaddingBottom();// 自己测量的高度 // 记录每一行的宽度和高度 int lineWidth = 0; int lineHeight = 0; // 每行中的View的个数 int lineViewCount = 0; // 每行中View的宽度的最大值 int lineViewMaxWidth = 0; // 获取子view的个数 int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); boolean lastChild = i == childCount - 1; // 不占控件的View跳过 if (child.getVisibility() == View.GONE) { if (lastChild) { width = Math.max(width, lineWidth); height += lineHeight; } continue; } // 测量子View的宽和高 measureChild(child, widthMeasureSpec, heightMeasureSpec); MarginLayoutParams lp = (MarginLayoutParams) child .getLayoutParams(); int childWidthMode = MeasureSpec.AT_MOST; // 子View占据的宽度 int childWidthSize = child.getMeasuredWidth(); int childHeightMode = MeasureSpec.AT_MOST; // 子View占据的高度 int childHeightSize = child.getMeasuredHeight(); if (lp.height >= 0) { childHeightMode = MeasureSpec.EXACTLY; // childHeightSize = lp.height; } else if (modeHeight == MeasureSpec.UNSPECIFIED) { childHeightMode = MeasureSpec.UNSPECIFIED; childHeightSize = 0; } // 测量子View的宽和高 child.measure(MeasureSpec.makeMeasureSpec(childWidthSize, childWidthMode), MeasureSpec.makeMeasureSpec( childHeightSize, childHeightMode)); int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; lineViewMaxWidth = lineViewMaxWidth == 0 ? childWidth : Math.max( lineViewMaxWidth, childWidth); // 换行时候 if (lineWidth + childWidth > sizeWidth || (lineMaxColumn > 0 && lineViewCount >= lineMaxColumn) || (!checkAddCurLineViews(sizeWidth, lineViewMaxWidth, lineViewCount))) { // 对比得到最大的宽度 width = Math.max(width, lineWidth); // 重置lineWidth lineWidth = childWidth; // 记录行高 height += lineHeight; lineHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; lineViewCount = 1; lineViewMaxWidth = childWidth; } else {// 不换行情况 // 叠加行宽 lineWidth += childWidth; // 得到最大行高 lineHeight = Math.max(lineHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); lineViewCount++; } // 处理最后一个子View的情况 if (lastChild) { width = Math.max(width, lineWidth); height += lineHeight; } } // wrap_content width += getPaddingLeft() + getPaddingRight(); setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth : width, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight : height); // 显示的大小 }
重新计算当前行中所有的View的宽度,宽度为所有View平分当前的FlowView的宽度
private void reMeasureLineViews(List<View> lineViews) { // 当前行的View的个数 int count = lineViews.size(); if (count == 0) { return; } // 当前行中每个View占用的宽度 int itemWidth = (this.getMeasuredWidth() - this.getLeftPaddingOffset() - this .getRightPaddingOffset()) / count; for (int i = 0; i < count; i++) { View child = lineViews.get(i); MarginLayoutParams lp = (MarginLayoutParams) child .getLayoutParams(); int childWidthSize = itemWidth - lp.leftMargin - lp.rightMargin; int childHeightSize = child.getMeasuredHeight(); child.measure(MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec( childHeightSize, MeasureSpec.EXACTLY)); } }
重新测量最后一行
onLayout中完成对所有childView的位置以及大小的指定
private void reMeasureLineViews(List<View> lineViews, int prveLineViewCount) { // 当前行的View的个数 int count = lineViews.size(); if (count == 0) { return; } // 只有一行时,设置上一行的个数为最大个数 prveLineViewCount = prveLineViewCount == 0 ? lineMaxColumn : prveLineViewCount; // 如果当前行中View的个数大于等于上一行中View的个数时,所有View的宽度为总宽度的平均值 if (prveLineViewCount <= count) { reMeasureLineViews(lineViews); return; } // 上一行中View的个数为基数并且当前行中View的个数大于上一行中View的个数的一半时,所有View的宽度为总宽度的平均值 else if (prveLineViewCount % 2 == 1 && count > (int) (prveLineViewCount * 1.0 / 2)) { reMeasureLineViews(lineViews); return; } // 上一行中每个View占用的宽度 int prveItemWidth = (this.getMeasuredWidth() - this.getLeftPaddingOffset() - this.getRightPaddingOffset()) / prveLineViewCount; // 当前行中的最大宽度 int lineViewMaxWidth = 0; for (int i = 0; i < count; i++) { View child = lineViews.get(i); MarginLayoutParams lp = (MarginLayoutParams) child .getLayoutParams(); lineViewMaxWidth = Math.max(lineViewMaxWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); } // 如果当前行中View的最大宽度大于上一行的平均值时,当前行中每个View的宽度为上一行平均值的2倍,否则为上一行的平均值。 int itemWidth = prveItemWidth; if (prveItemWidth < lineViewMaxWidth) { if (prveLineViewCount % 2 == 0 || count > (int) (prveLineViewCount * 1.0 / 2)) { reMeasureLineViews(lineViews); return; } itemWidth = 2 * prveItemWidth; } for (int i = 0; i < count; i++) { View child = lineViews.get(i); MarginLayoutParams lp = (MarginLayoutParams) child .getLayoutParams(); int childWidthSize = itemWidth - lp.leftMargin - lp.rightMargin; int childHeightSize = child.getMeasuredHeight(); child.measure(MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec( childHeightSize, MeasureSpec.EXACTLY)); } }
检查是否能够添加到当前行中
private boolean checkAddCurLineViews(int totalWidth, int maxWidth, int curCount) { if (totalWidth / (curCount + 1) > maxWidth) { return true; } return false; } @SuppressLint("DrawAllocation") @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { mAllChildViews.clear(); mLineHeight.clear(); // 获取当前ViewGroup的宽度 int width = getWidth(); int lineWidth = 0; int lineHeight = 0; // int lineItemMaxWidth = 0; // int prevLineViewCount = 0; // 记录当前行的view List<View> lineViews = new ArrayList<View>(); int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); boolean lastChild = i == childCount - 1; MarginLayoutParams lp = (MarginLayoutParams) child .getLayoutParams(); int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); // 如果需要换行 if ((lineViews.size() != 0 && childWidth + lineWidth + lp.leftMargin + lp.rightMargin > width) || (lineMaxColumn > 0 && lineViews.size() >= lineMaxColumn)) { mAllChildViews.add(lineViews); // 记录LineHeight mLineHeight.add(lineHeight); // 记录当前行的Views reMeasureLineViews(lineViews); prevLineViewCount = lineViews.size(); // 重置行的宽高 lineWidth = 0; lineHeight = childHeight + lp.topMargin + lp.bottomMargin; // 重置view的集合 lineViews = new ArrayList<View>(); lineItemMaxWidth = 0; } lineItemMaxWidth = lineItemMaxWidth == 0 ? childWidth + lp.leftMargin + lp.rightMargin : Math.max( lineItemMaxWidth, childWidth + lp.leftMargin + lp.rightMargin); if (!checkAddCurLineViews(width, lineItemMaxWidth, lineViews.size())) { if (lineViews.size() != 0) { mAllChildViews.add(lineViews); } // 记录LineHeight mLineHeight.add(lineHeight); // 记录当前行的Views reMeasureLineViews(lineViews); prevLineViewCount = lineViews.size(); // 重置行的宽高 lineWidth = 0; lineHeight = childHeight + lp.topMargin + lp.bottomMargin; // 重置view的集合 lineViews = new ArrayList<View>(); lineItemMaxWidth = childWidth + lp.leftMargin + lp.rightMargin; } lineWidth += childWidth + lp.leftMargin + lp.rightMargin; lineHeight = Math.max(lineHeight, childHeight + lp.topMargin + lp.bottomMargin); lineViews.add(child); if (lastChild) { // 处理最后一行 mLineHeight.add(lineHeight); mAllChildViews.add(lineViews); // 记录当前行的Views reMeasureLineViews(lineViews, prevLineViewCount); } } // 设置子View的位置 int left = 0; int top = 0; // 获取行数 int lineCount = mAllChildViews.size(); for (int i = 0; i < lineCount; i++) { // 当前行的views和高度 lineViews = mAllChildViews.get(i); lineHeight = mLineHeight.get(i); for (int j = 0; j < lineViews.size(); j++) { View child = lineViews.get(j); // 判断是否显示 if (child.getVisibility() == View.GONE) { continue; } MarginLayoutParams lp = (MarginLayoutParams) child .getLayoutParams(); int cLeft = left + lp.leftMargin; int cTop = top + lp.topMargin; int cRight = cLeft + child.getMeasuredWidth(); int cBottom = cTop + child.getMeasuredHeight(); // 进行子View进行布局 child.layout(cLeft, cTop, cRight, cBottom); left += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; } left = 0; top += lineHeight; } }
与当前ViewGroup对应的LayoutParams
@Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(), attrs); }
完整代码下载地址
0 0
- 自定义可设置每行最多显示数目,每个子View宽度等分的FlowView
- android自定义view----等分饼图,实现每个块中间的间隔
- CSS3可伸缩框属性,可用于等分显示子元素或按比例显示子元素的大小
- Android圆形ImageView,可设置最多两个宽度不同且颜色不同的圆形边框
- HorizontalScrollView动态添加子view,并且设置每个子view的点击事件
- 选择每个订单的每种费用的数目,每行纪录为一个订单信息
- TextView 最多显示2行,每行最多8个字,多余的显示....
- 自定义view的宽度问题
- 仿微信消息数目提示的自定义view
- jQuery设置指定表格每行第一列的宽度
- 自定义ViewGroup,子View可对换位置
- 自定义eclipse一行显示字符串的数目
- 设置Dialog的显示宽度
- Android listView 设置item显示的数目
- 最多共线点的数目
- 自定义View的显示
- Android Dialog设置宽度显示不正常的问题(自定义设置Dialog的宽高)
- Android动态设置view的高度宽度
- Hexo 我的博客
- React-Natvie 介绍大全
- 自定义字符串加密
- [Java视频笔记]day14
- 欢迎使用CSDN-markdown编辑器
- 自定义可设置每行最多显示数目,每个子View宽度等分的FlowView
- mysql的查询、子查询及连接查询
- 找出一组数中指出现一次的数2
- P1071 LCIS 最长公共上升子序列
- linux文件描述符和套接字的问题
- 下面介绍几种查看linux版本信息的方法和GCC版本
- Python 通过调用接口获取公交信息
- win7系统右键没有新建文件夹选项
- 关于Myeclipse2015的破解的详细介绍