前言:实现多元化的标签显示的页面,所有的子控件自动排版,类似各大网站的热搜效果!先贴上一张效果图供大家赏鉴!
先来说一下实现步骤:
一:定义一个容器:
定义一个什么容器呢?存放子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五:XML文件中去引用
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
这个类放在最外层,将需要设置的子控件包裹就可以了!