package com.fuban.user.views; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import com.fuban.user.views.textbanner.utils.DisplayUtils; import java.util.ArrayList; import java.util.List; public class CenterFlowLayout extends ViewGroup { //默认item的spacing private static final int DEFAULT_CHILD_SPACING = 0; //默认item的topMargin值 private static final int DEFAULT_ROW_SPACING = 0; //子View之间的间距 private int mChildSpacing = DEFAULT_CHILD_SPACING; //子View的marginTop值 private int mRowSpacing = DEFAULT_ROW_SPACING; //用来存储每行item所占用的宽度的总和 private List itemLineWidth = new ArrayList<>(); //用来存储每行item的个数 private List itemLineNum = new ArrayList<>(); //用来记录item所占用的总行数 private int mRowTotalCount = 0; public CenterFlowLayout(Context context) { this(context, null); } public CenterFlowLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CenterFlowLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mChildSpacing = DisplayUtils.dip2px(context,8); mRowSpacing = DisplayUtils.dip2px(context,12); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { //获取父View的paddingLeft值 int childLeft = getPaddingLeft(); //获取父View的paddingTop值 int childTop = getPaddingTop(); //获取去除paddingRight之后的宽度 int childRight = getMeasuredWidth() - getPaddingRight(); //获取实际子View可用的宽度 int availableWidth = childRight - childLeft; //子View初始时left值 int curLeft; //子View初始时的top值 int curTop = childTop; //单行中最大子View的高度 int maxHeight = 0; //子View的高度 int childHeight; //子View宽度 int childWidth; //父View中子View的index int childIndex = 0; for (int j = 0; j < mRowTotalCount; j++) { //获取单行中子View的个数 Integer childNum = itemLineNum.get(j); //初始化子View的left值 curLeft = childLeft + (availableWidth - itemLineWidth.get(j)) / 2; //竖直方向上的margin值 int verticalMargin = 0; for (int i = 0; i < childNum; i++) { //获取ViewGroup中的子View View child = getChildAt(childIndex++); //跳过可见性为GONE的子View if (child.getVisibility() == View.GONE) { continue; } //获取子View的宽度 childWidth = child.getMeasuredWidth(); //获取子View的高度 childHeight = child.getMeasuredHeight(); //获取子View的LayoutParams MarginLayoutParams params = (CenterLayoutParams) child.getLayoutParams(); int marginRight = 0, marginTop = 0, marginBottom; if (params instanceof MarginLayoutParams) { //获取子View的marginRight值 marginRight = params.rightMargin; //获取子View的marginTop值 marginTop = params.topMargin; //获取子View的marginBottom值 marginBottom = params.bottomMargin; //获取子View上下间距的和 if (childNum > 1 && i == 0) { verticalMargin = marginTop + marginBottom; } } //对子View进行布局 child.layout(curLeft, curTop, curLeft + childWidth, curTop + childHeight); //找到单行中子View的最高高度 if (maxHeight < childHeight) { maxHeight = childHeight; } //叠加left值,向右一次排列 curLeft += childWidth + mChildSpacing + marginRight; } //换行时的高度 curTop += maxHeight + mRowSpacing + verticalMargin; //最大高度重置 maxHeight = 0; } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //将属性值重置 itemLineNum.clear(); itemLineWidth.clear(); mRowTotalCount = 0; //获取ViewGroup的宽度 int widthSize = MeasureSpec.getSize(widthMeasureSpec); //获取ViewGroup的宽度mode int widthMode = MeasureSpec.getMode(widthMeasureSpec); //获取ViewGroup的高度 int heightSize = MeasureSpec.getSize(heightMeasureSpec); //获取ViewGroup的高度mode int heightMode = MeasureSpec.getMode(heightMeasureSpec); //获取ViewGroup的可用宽度 int rowLength = widthSize - getPaddingLeft() - getPaddingRight(); //测量的宽度 int measuredWidth = 0; //测量的高度 int measuredHeight = 0; //子View最大的宽度 int maxWidth = 0; //子View最大的高度 int maxHeight = 0; //子View的总行数 int rowCount = 0; //子View的个数 int childCount = getChildCount(); //单行子View的宽度 int rowWidth = 0; //子View宽度 int childWidth; //子View高度 int childHeight; //单行中子View的个数 int childNumInRow = 0; //最后一行第一个子View的index int tempIndex = 0; //除最后一行外,其他单行中子View的总和 int exceptLastRowNum = 0; for (int i = 0; i < childCount; i++) { View child = getChildAt(i); if (child.getVisibility() == View.GONE) { continue; } CenterLayoutParams params = (CenterLayoutParams) child.getLayoutParams(); int marginRight = 0, marginTop = 0, marginBottom = 0; if (params instanceof MarginLayoutParams) { measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, measuredHeight); marginRight = params.rightMargin; marginTop = params.topMargin; marginBottom = params.bottomMargin; } else { measureChild(child, widthMeasureSpec, heightMeasureSpec); } //子View本身的宽度+子View之间的间距+子View的marginRight值(我这么写偷懒了,mChildSpacing和marginRight不用同时设置值) childWidth = child.getMeasuredWidth() + mChildSpacing + marginRight; //子View本身的高度+子View的marginTop值+子View的marginBottom+marginTop值(我这么写偷懒了,mRowSpacing和marginBottom + marginTop不用同时设置值) childHeight = child.getMeasuredHeight() + mRowSpacing + marginBottom + marginTop; //叠加子View的宽度 rowWidth += childWidth; //取出最大的宽度 maxWidth += Math.max(maxWidth, childWidth); //判断是否需要换行 if (measuredWidth + childWidth > rowLength) { //循环后,就可获取,最后一行中第一个子View的index tempIndex = i; //获取单行的宽度 rowWidth = rowWidth - childWidth - mChildSpacing - marginRight; //存储单行的宽度 itemLineWidth.add(rowWidth); //设置下一行宽度为第一个子View的宽度 rowWidth = childWidth; //行数自增 ++rowCount; //保存测量的宽度 measuredWidth = childWidth; //叠加子View的高度 maxHeight += childHeight; //存储单行中子View的个数 itemLineNum.add(childNumInRow); //叠加获取除了最后一行外,其他行子View的个数的总和 exceptLastRowNum += childNumInRow; //重置子View的个数,因为已经要换行了 childNumInRow = 1; } else { //叠加宽度 measuredWidth += childWidth; //叠加单行中的子View个数 ++childNumInRow; //计算出最大的高度 maxHeight = Math.max(maxHeight, childHeight); } } //最后一行的宽度 int lastRowWidth = 0; int singleHorizalMargin = 0; for (int i = tempIndex; i < childCount; i++) { View child = getChildAt(i); int horizalMargin = 0; CenterLayoutParams params = (CenterLayoutParams) child.getLayoutParams(); if (params instanceof MarginLayoutParams) { //获取子View的marginRight singleHorizalMargin = horizalMargin = params.rightMargin; } //叠加最后一行中所有子View的宽度 lastRowWidth += child.getMeasuredWidth() + mChildSpacing + horizalMargin; } //获取最后一行子View的个数 int lastChildCount = childCount - exceptLastRowNum; //减去多余的marginRight或mChildSpacing,得到最终的宽度 lastRowWidth -= mChildSpacing == 0 ? singleHorizalMargin : mChildSpacing; //保存最后一行的宽度值 itemLineWidth.add(lastRowWidth); //保存最后一行子View的个数 itemLineNum.add(lastChildCount); //子View的总行数 mRowTotalCount = rowCount + 1; //子View的最大宽度+ViewGroup中的paddingLeft和paddingRight值 maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()) + getPaddingRight() + getPaddingLeft(); //子View的最大高度+ViewGroup中的paddingTop和paddingBottom值 maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()) + getPaddingTop() + getPaddingBottom(); //根据widthMode设置width值 measuredWidth=widthMode==MeasureSpec.EXACTLY?widthSize:maxWidth; //根据heightMode设置height值 measuredHeight=heightMode==MeasureSpec.EXACTLY?heightSize:maxHeight; //设置ViewGroup的宽高 setMeasuredDimension(resolveSize(measuredWidth, widthMeasureSpec), resolveSize(measuredHeight, heightMeasureSpec)); } /** * 设置子View的间距 * @param childSpacing */ public void setChildSpacing(int childSpacing){ mChildSpacing=childSpacing; requestLayout(); } /** * 设置子View的marginTop值 * @param rowSpacing */ public void setRowSpacing(int rowSpacing){ mRowSpacing=rowSpacing; requestLayout(); } @Override protected LayoutParams generateLayoutParams(LayoutParams p) { return new CenterLayoutParams(p); } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new CenterLayoutParams(getContext(), attrs); } /** * @param p * @return */ @Override protected boolean checkLayoutParams(LayoutParams p) { return p instanceof CenterLayoutParams; } /** * 因为需要获取子View的margin值,所以这里需要重写一下该方法 */ public static class CenterLayoutParams extends MarginLayoutParams { public CenterLayoutParams(MarginLayoutParams source) { super(source); } public CenterLayoutParams(LayoutParams source) { super(source); } public CenterLayoutParams(Context c, AttributeSet attrs) { super(c, attrs); } public CenterLayoutParams(int width, int height) { super(width, height); } } }