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

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

Android自定义ViewGroup之流式布局的实现_第1张图片

先来说一下实现步骤:

一:定义一个容器:

      定义一个什么容器呢?存放子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文件中去引用

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

你可能感兴趣的:(Android)