返回

自定义View之简版流式布局,解锁灵活布局新姿势

Android

自定义View之简版流式布局

简版流式布局概述

流式布局是一种常用布局,它的特点是根据子View的实际大小动态摆放子View,排布方式较为灵活。目前安卓原生系统中,只有RecyclerView提供类似的功能,然而RecyclerView有一个比较致命的缺点,那就是它只能在一个方向上布局,而无法在两个方向上同时布局。本文实现的简版流式布局除了继承流式布局的特点之外,还能够在两个方向上同时布局,从而满足更为复杂的布局需求。

简版流式布局实现原理

简版流式布局的实现原理并不复杂,大致分为以下几个步骤:

1. 测量

遍历子View,父容器的MeasureSpec和子View的布局参数,测量子View。在测量过程中,记录每一个View的宽和高。

2. 布局

遍历过程中累加遍历的view的宽度值,如果宽度值大于等于父容器的宽度值,则换行。换行之后,将宽度值重置为0,并继续遍历子View,累加宽度值。

3. 绘制

根据子View的测量结果,将其绘制到画布上。

简版流式布局使用场景

简版流式布局可以用于多种场景,例如:

  • 标签布局:可以根据标签的实际长度动态摆放标签,使标签布局更美观。
  • 图片布局:可以根据图片的实际大小动态摆放图片,使图片布局更紧凑。
  • 商品布局:可以根据商品的实际大小动态摆放商品,使商品布局更具吸引力。

简版流式布局代码实现

public class FlowLayout extends ViewGroup {

    private int mLineSpacing; // 行间距
    private int mColumnSpacing; // 列间距

    public FlowLayout(Context context) {
        super(context);
    }

    public FlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
        mLineSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_lineSpacing, 0);
        mColumnSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_columnSpacing, 0);
        a.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int selfWidth = MeasureSpec.getSize(widthMeasureSpec);
        int selfHeight = MeasureSpec.getSize(heightMeasureSpec);

        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        int paddingRight = getPaddingRight();
        int paddingBottom = getPaddingBottom();

        int width = 0;
        int height = 0;
        int lineWidth = 0;
        int lineHeight = 0;

        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }

            measureChild(child, widthMeasureSpec, heightMeasureSpec);

            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();

            if (lineWidth + childWidth + mColumnSpacing > selfWidth - paddingLeft - paddingRight) {
                width = Math.max(width, lineWidth);
                height += lineHeight + mLineSpacing;

                lineWidth = childWidth;
                lineHeight = childHeight;
            } else {
                lineWidth += childWidth + mColumnSpacing;
                lineHeight = Math.max(lineHeight, childHeight);
            }
        }

        width += lineWidth;
        height += lineHeight;

        setMeasuredDimension(resolveSize(width, widthMeasureSpec), resolveSize(height, heightMeasureSpec));
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int left = getPaddingLeft();
        int top = getPaddingTop();
        int right = r - l - getPaddingRight();
        int bottom = b - t - getPaddingBottom();

        int lineWidth = 0;
        int lineHeight = 0;

        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }

            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();

            if (lineWidth + childWidth + mColumnSpacing > right) {
                top += lineHeight + mLineSpacing;
                left = getPaddingLeft();

                lineWidth = childWidth;
                lineHeight = childHeight;
            } else {
                lineWidth += childWidth + mColumnSpacing;
                lineHeight = Math.max(lineHeight, childHeight);
            }

            child.layout(left, top, left + childWidth, top + childHeight);

            left += childWidth + mColumnSpacing;
        }
    }
}

总结

简版流式布局是一种常用布局,它的特点是根据子View的实际大小动态摆放子View,排布方式较为灵活。本文实现的简版流式布局除了继承流式布局的特点之外,还能够在两个方向上同时布局,从而满足更为复杂的布局需求。简版流式布局可以用于多种场景,例如:标签布局、图片布局、商品布局等。