Android自定义ViewGroup之流式布局的实现

时间:2021-10-04 05:27:27

前言:实现多元化的标签显示的页面,所有的子控件自动排版,类似各大网站的热搜效果!先贴上一张效果图供大家赏鉴!

Android自定义ViewGroup之流式布局的实现

先来说一下实现步骤:

一:定义一个容器:

      定义一个什么容器呢?存放子View的容器,这个容器可以是ViewGroup也可以Layout,我这里用的是ViewGroup

定义一个类继承于ViewGroup,实现其相应的构造方法,

二:对子View的宽高进行测量:
在onMeasure方法中对子View进行测量以及相应的判断

//测量子控件的宽高
measureChildren(widthMeasureSpec, heightMeasureSpec);
int wSize = MeasureSpec.getSize(widthMeasureSpec);
int hSize = MeasureSpec.getSize(heightMeasureSpec);
int wMode = MeasureSpec.getMode(widthMeasureSpec);
int hMode = MeasureSpec.getMode(heightMeasureSpec);
int aWidth = 0;//控件的总宽度
int aHeight = 0;//控件的总高度
int lineWidth = 0;//当前行的宽度
int lineHeight = 0;//当前行的高度

//遍历循环每个子控件
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);

MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams();
//当前控件所占的宽度
int width = view.getMeasuredWidth() + marginLayoutParams.leftMargin + marginLayoutParams.rightMargin;
//当前控件所占的高度
int height = view.getMeasuredHeight() + marginLayoutParams.bottomMargin + marginLayoutParams.topMargin;
//判断控件是否需要换行
if (lineWidth + width <= wSize) {
//宽度累加
lineWidth += width;
//高度取最大值
lineHeight = Math.max(lineHeight, height);
} else {
//需要换行
//将当前行的宽高情况,添加进总宽高中
aWidth = Math.max(aWidth, lineWidth);
aHeight += lineHeight;
//重新开启新的一行
lineWidth = width;
lineHeight = height;
}
if (i == getChildCount() - 1) {
//强最后一行的宽高情况添加进总宽高
aWidth = Math.max(aWidth, lineWidth);
aHeight += lineHeight;
}
}
Log.d("print", "onMeasure: " + aWidth + " " + aHeight);
setMeasuredDimension(
wMode == MeasureSpec.EXACTLY ? wSize : aWidth,
hMode == MeasureSpec.EXACTLY ? hSize : aHeight
);

三:控制子View控件的位置的摆放:

设置每个子控件的摆放位置以及控制,在onLayout方法中去控制

int lineWidth = 0;//当前行的宽度
int lineHeight = 0;//当前行的高度
int aHeight = 0;//空间的总高度
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
MarginLayoutParams layoutParams = (MarginLayoutParams) view.getLayoutParams();
//当前空间的宽度和高度
int width = view.getMeasuredWidth();
int height = view.getMeasuredHeight();
if (lineWidth + width + layoutParams.leftMargin + layoutParams.rightMargin > getWidth()) {
//需要换行
aHeight += lineHeight;
lineWidth = 0;
lineHeight = 0;
}
l = layoutParams.leftMargin + lineWidth;
t = layoutParams.topMargin + aHeight;
r = layoutParams.rightMargin + width + lineWidth;
b = layoutParams.bottomMargin + aHeight + height;
//把当前控件的宽高数据加入到行的宽高数据中
lineWidth += width + layoutParams.leftMargin + layoutParams.rightMargin;
lineHeight = Math.max(lineHeight, height + layoutParams.topMargin + layoutParams.bottomMargin);

//摆放当前控件
view.layout(l, t, r, b);
}

四:重写generateLayoutParams方法控制这个布局使用的布局参数:

默认是ViewGroup.LayoutParams

@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
五:XML文件中去引用

这个类放在最外层,将需要设置的子控件包裹就可以了!