Android 自定义流式布局

Android 自定义流式布局,第1张

先上效果

具体实现请参考如下:

import android.content.Context;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;

public class WaterfallFlowLayout3 extends ViewGroup {
    // 存储每一行的View
    private List> mEachLineView = new ArrayList<>();

    // 每一行的宽度,以每行最高View的宽度为每行的宽度
    private List mEachLineHeight = new ArrayList<>();

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

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

    public WaterfallFlowLayout3(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public WaterfallFlowLayout3(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

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

    @Override
    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
        // onLayout其实就是获取左、上、右、下四个值,然后设置此4个值
        int left;
        int top;
        int right;
        int bottom;

        // 当前行的左和上位置
        int curLeft = 0;
        int curTop = 0;

        // 一行一行的遍历
        for (int index = 0; index < mEachLineView.size(); index++) {
            List listView = mEachLineView.get(index);
            if (listView == null || listView.isEmpty()) {
                continue;
            }

            // 遍历一行的每一个View
            for (View view : listView) {
                if (view == null) {
                    continue;
                }

                // 获取间距布局参数
                MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams();
                if (marginLayoutParams == null) {
                    continue;
                }

                // 获取当前View的四个点的坐标值
                left = curLeft + marginLayoutParams.leftMargin;
                top = curTop + marginLayoutParams.topMargin;
                right = left + view.getMeasuredWidth();
                bottom = top + view.getMeasuredHeight();

                // 最终就是调用这个方法进行摆放
                view.layout(left, top, right, bottom);

                // 将当前的左坐标位置往后移动,用于下一个view的计算
                curLeft += view.getMeasuredWidth() + marginLayoutParams.leftMargin + marginLayoutParams.rightMargin;
            }

            // 新的一行要从左边开始
            curLeft = 0;

            // 换行要将顶部的坐标往下移动
            curTop += mEachLineHeight.get(index);
        }

        mEachLineHeight.clear();
        mEachLineView.clear();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mEachLineView.clear();
        mEachLineHeight.clear();

        // 1.获取父容器的参数
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        // 2.onMeasure的目的也就是获取本View的宽和高的值,此处先定义变量
        int measureWidth = 0;
        int measureHeight = 0;

        // 3.根据父容器的模式来获设置本View的宽和高,也就是2中的数值
        // 3.1 宽和高都是match_parent
        if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {
            // 如果都是match_parent那就不需要计算,直接设置为父容器的宽和高
            measureWidth = widthSize;
            measureHeight = heightSize;
        } else {
            // 用来统计当前行的宽和高
            int curLineWidth = 0;
            int curLineMaxHeight = 0;

            // 当前子view的宽度和高度
            int curChildWidth = 0;
            int curChildHeight = 0;

            // 每行摆放的子view的集合
            List curLineViews = new ArrayList<>();

            // 获取该容器中所有的子View进行遍历计算
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                View childView = getChildAt(i);
                if (childView == null) {
                    continue;
                }

                // 首先测量子View
                measureChild(childView, widthMeasureSpec, heightMeasureSpec);

                // 获取子view的宽高
                MarginLayoutParams marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();

                // 计算当前子view的宽和高
                curChildWidth = marginLayoutParams.leftMargin + childView.getMeasuredWidth() + marginLayoutParams.rightMargin;
                curChildHeight = marginLayoutParams.topMargin + childView.getMeasuredHeight() + marginLayoutParams.bottomMargin;

                // 判断如果当前行的宽度和已经大于父容器的宽度,那么就需要换行
                if (curChildWidth + curLineWidth > widthSize) {
                    // 需要换行,先将换行前最后一行参数(也就是当前行)
                    // 总的View的宽度和高度赋值后(这也是我们测量获取的两个目标值),然后保存当前行的信息
                    measureWidth = Math.max(measureWidth, curLineWidth);
                    measureHeight += curLineMaxHeight;
                    mEachLineView.add(curLineViews);
                    mEachLineHeight.add(curLineMaxHeight);

                    // 创建新的一行
                    curLineViews = new ArrayList<>();

                    // 将当前的子view放到新一行的开头
                    curLineViews.add(childView);

                    // 新的一行目前只摆放了一个view所以当前行的宽度和高度都是当前子view的宽和高
                    curLineWidth = curChildWidth;
                    curLineMaxHeight = curChildHeight;
                } else {
                    // 不需要换行
                    curLineViews.add(childView);
                    curLineWidth += curChildWidth;
                    curLineMaxHeight = Math.max(curLineMaxHeight, curChildHeight);
                }

                // 最后一个处在最后一行上,因最后一行因为不需要换行,所以最后一行的view数列没有被添加到数列中,此处需要单独处理
                if (i == childCount - 1) {
                    // 最后一行同样也要参与到想要获取的目标值之中
                    measureWidth = Math.max(measureWidth, curLineWidth);
                    measureHeight += curLineMaxHeight;

                    mEachLineView.add(curLineViews);
                    mEachLineHeight.add(curChildHeight);
                }
            }
        }


        // 最终目的就是在此处设置已经计算好的本容器的宽和高
        setMeasuredDimension(measureWidth, measureHeight);
    }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/584483.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-12
下一篇 2022-04-12

发表评论

登录后才能评论

评论列表(0条)

保存