FlowLayout 自定义流式布局
2021-03-23 本文已影响0人
马路牙子666
上效果图
image.pngpackage com.zt.flowlayout.weget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
/**
* 自定义 流式布局
*/
public class FlowLayout extends ViewGroup {
//横向分割 宽度
private int mHorizontalSpacing = dp2px(16);
//纵向分割 宽度
private int mVerticalSpacing = dp2px(8);
private List<List<View>> allLineViews = new ArrayList<>();
private List<Integer> lineHeights = new ArrayList<>();
/**
* 通过new 创建对象
*
* @param context
*/
public FlowLayout(Context context) {
super(context);
}
/**
* 通过反射
*
* @param context
* @param attrs
*/
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 防止内存抖动 , 每次清空而不是 重新创建
*/
private void clearMeasureParams() {
allLineViews.clear();
lineHeights.clear();
}
/**
* 框架的宽高
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
clearMeasureParams();
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
//获取父控件的宽度高度
int selfWidth = MeasureSpec.getSize(widthMeasureSpec); //ViewGroup解析的父亲给我的宽度
int selfHeight = MeasureSpec.getSize(heightMeasureSpec); // ViewGroup解析的父亲给我的高度
//保存当前行所有view
List<View> lineViews = new ArrayList<>();
//当前行高度
int lineHeight = 0;
//当前行宽度
int lineWidthUsed = 0;
int parentNeededWidth = 0; // measure过程中,子View要求的父ViewGroup的宽
int parentNeededHeight = 0; // measure过程中,子View要求的父ViewGroup的高
//获取所有孩子计算 递归计算孩子所需宽高
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
LayoutParams lp = childView.getLayoutParams();
int childMeasureSpecWidth = getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight, lp.width);
int childMeasureSpecHeight = getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom, lp.height);
childView.measure(childMeasureSpecWidth, childMeasureSpecHeight);
//当前view 的具体宽 高
int childMeasuredWidth = childView.getMeasuredWidth();
int childMeasuredHeight = childView.getMeasuredHeight();
//判断是否需要换行
if (lineWidthUsed + childMeasuredWidth + mHorizontalSpacing > selfWidth) {
//保存当前行 所有控件
allLineViews.add(lineViews);
//保存当前行高
lineHeights.add(lineHeight);
//记录 当前父控件所需宽高
parentNeededWidth = Math.max(parentNeededWidth, lineWidthUsed);
parentNeededHeight += lineHeight + mVerticalSpacing;
//当前行 相关保存数据 重置
lineViews = new ArrayList<>();
lineHeight = 0;
lineWidthUsed = 0;
}
//保存当前行所有控件
lineViews.add(childView);
//计算出最大高度//如每个控件高度不一样
lineHeight = Math.max(lineHeight, childMeasuredHeight);
//当前行宽度
lineWidthUsed = lineWidthUsed + childMeasuredWidth + mHorizontalSpacing;
if (i == childCount - 1) {//防止最后一个控件 是需要换行
//保存当前行 所有控件
allLineViews.add(lineViews);
//保存当前行高
lineHeights.add(lineHeight);
//记录 当前父控件所需宽高
parentNeededWidth = Math.max(parentNeededWidth, lineWidthUsed);
parentNeededHeight += lineHeight;
}
}
//再度量自己,保存
//根据子View的度量结果,来重新度量自己ViewGroup
// 作为一个ViewGroup,它自己也是一个View,它的大小也需要根据它的父亲给它提供的宽高来度量
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int realWidth = (widthMode == MeasureSpec.EXACTLY) ? selfWidth : parentNeededWidth;
int realHeight = (heightMode == MeasureSpec.EXACTLY) ? selfHeight : parentNeededHeight;
setMeasuredDimension(realWidth, realHeight);
}
/**
* 确定 每个子view 的位置
*
* @param changed
* @param l
* @param t
* @param r
* @param b
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//确定第一个控件的xy位置 就是 paddingTop 和 paddingLeft w
int curL = getPaddingLeft();
int curT = getPaddingTop();
int lineCount = allLineViews.size();
for (int i = 0; i < lineCount; i++) {
//获取所有行数 的控件
List<View> views = allLineViews.get(i);
//获取当前行高
int height = lineHeights.get(i);
//当前行数有几个控件
int nowLienView = views.size();
for (int j = 0; j < nowLienView; j++) {
//确定第一个控件位置
View view = views.get(j);
//调用过 onMeasure 这个就有值,
//右边位置 需要当前宽度 加上左边位置
int curR = view.getMeasuredWidth() + curL;
//下边位置 需要当前高度 加上上边位置
int curB = view.getMeasuredHeight() + curT;
view.layout(curL, curT, curR, curB);
//第二个或者之后的 x 轴需要改变也就是 curL 需要加上当前控件的宽 和 横向分割宽度
curL = curR + mHorizontalSpacing;
}
//第二行 left 需要重置,top 需要加上 上一行的高度 和 纵向分割宽度
curL = getPaddingLeft();
curT = curT + height + mVerticalSpacing;
}
}
public int dp2px(float dpValue) {
float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
}