自适应布局FlowLayout

一图顶千言:
自适应布局FlowLayout_第1张图片

很明显,这是一个自定义ViewGroup,三个步骤,测量(onMeasure),布局(onLayout),绘制(onDraw,本例中用不到绘制哦);不叨叨,上代码
1.构造方法,在xml中引用,要实现2个参数的构造方法

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

2.onMeasrue,测量
FlowLayout的宽是:match_parent;高是:wrap_content,你知道的,自定义ViewGroup需要自己实现wrap_content,以及子view的margin(这个大家都很清楚了,就不赘述了。)

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //默认的高度,宽度以及模式
        int widthMeasureSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMeasureMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMeasureSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMeasureMode = MeasureSpec.getMode(heightMeasureSpec);

        //viewgroup总的高度和宽度
        int height = 0;
        int width = 0;
        //每一行的宽度和高度
        int lineHeight = 0;
        int lineWidth = 0;

        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childAt = getChildAt(i);
            //测量子view
            measureChild(childAt, widthMeasureSpec, heightMeasureSpec);
            //获取子view的宽高
            //LayoutParams layoutParams1 = childAt.getLayoutParams();
            MarginLayoutParams layoutParams = (MarginLayoutParams) childAt.getLayoutParams();
            int childWidth = childAt.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;
            int childHeight = childAt.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;

            //开始测量
            //需要换行
            if (lineWidth + childWidth > widthMeasureSize) {
                width = Math.max(lineWidth, childWidth);
                height += lineHeight;

                //换行后的行高和行宽
                lineWidth = childWidth;
                lineHeight = childHeight;
            } else {
                //不需要换行
                lineHeight = Math.max(lineHeight, childHeight);
                lineWidth += childWidth;

                if (i == childCount - 1) {
                    height += lineHeight;
                    width = Math.max(width, lineWidth);
                }
            }

            setMeasuredDimension(widthMeasureMode == MeasureSpec.EXACTLY ? widthMeasureSize : width
                    , heightMeasureMode == MeasureSpec.EXACTLY ? heightMeasureSize : height);
        }
    }

  //LayoutParams是viewgroup提供给子view使用的,LayoutParams无法获取margin值,MarginLayoutParams可以获取margin值
    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

前面说了,我们需要自己实现margin和wrap_content
实现wrap_content:for循环,遍历所有的子view,子view的高的和就是FlowLayout的高;实现子view的margin,需要重写generateLayoutParams().往下看:

View childAt = getChildAt(i);
            //测量子view
            measureChild(childAt, widthMeasureSpec, heightMeasureSpec);
            //获取子view的宽高
            MarginLayoutParams layoutParams = (MarginLayoutParams) childAt.getLayoutParams();

这几行代码,分别是测量子View,获取子View的LayoutParams(别告诉我你不知道LayoutParams是啥),但是,默认情况下你的代码是这个样子的:

//LayoutParams layoutParams1 = childAt.getLayoutParams();

LayoutParams 不包含margin信息,无法获取margin值,所以我们需要转成:

MarginLayoutParams layoutParams = (MarginLayoutParams) childAt.getLayoutParams();

同时,重写:

@Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

原本该方法是返回LayoutParams 的,让他返回MarginLayoutParams,就是我们

MarginLayoutParams layoutParams = (MarginLayoutParams) childAt.getLayoutParams();

中获得的MarginLayoutParams 。
onLayout 布局

 @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childCount = getChildCount();
        int lineWidth = 0, lineHeight = 0, left = 0, top = 0;
        for (int i = 0; i < childCount; i++) {
            View childAt = getChildAt(i);
            MarginLayoutParams layoutParams = (MarginLayoutParams) childAt.getLayoutParams();
            int leftMargin = layoutParams.leftMargin;
            int rightMargin = layoutParams.rightMargin;
            int topMargin = layoutParams.topMargin;
            int bottomMargin = layoutParams.bottomMargin;
            int childWidth = childAt.getMeasuredWidth() + leftMargin + rightMargin;
            int childHeight = childAt.getMeasuredHeight() + topMargin + bottomMargin;

            //开始布局
            //换行
            if (lineWidth + childWidth > getMeasuredWidth()) {
                top += lineHeight;
                left = 0;
                //换行后,重新计算lineHeight,lineWidth
                lineWidth = childWidth;
                lineHeight = childHeight;
            } else {
                //不换行
                lineWidth += childWidth;
                lineHeight = Math.max(childHeight , lineHeight);
            }
            int cl = left + leftMargin;
            int cr = cl + childAt.getMeasuredWidth();
            int ct = top + topMargin;
            int cb = ct + childAt.getMeasuredHeight();
            childAt.layout(cl, ct, cr, cb);
            left += childWidth;
        }
    }

都是很简单的逻辑问题,我就不啰嗦了。

源码奉上

你可能感兴趣的:(Android)