美文网首页
FlowLayout 自定义流式布局

FlowLayout 自定义流式布局

作者: 马路牙子666 | 来源:发表于2021-03-23 11:27 被阅读0次

    上效果图

    image.png
    package com.zt.flowlayout.weget;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.View;
    import android.view.ViewGroup;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 自定义 流式布局
     */
    public class FlowLayout extends ViewGroup {
        //横向分割 宽度
        private int mHorizontalSpacing = dp2px(16);
        //纵向分割 宽度
        private int mVerticalSpacing = dp2px(8);
    
        private List<List<View>> allLineViews = new ArrayList<>();
        private List<Integer> lineHeights = new ArrayList<>();
    
    
        /**
         * 通过new 创建对象
         *
         * @param context
         */
        public FlowLayout(Context context) {
            super(context);
        }
    
        /**
         * 通过反射
         *
         * @param context
         * @param attrs
         */
        public FlowLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        /**
         * 防止内存抖动 , 每次清空而不是 重新创建
         */
        private void clearMeasureParams() {
            allLineViews.clear();
            lineHeights.clear();
        }
    
        /**
         * 框架的宽高
         *
         * @param widthMeasureSpec
         * @param heightMeasureSpec
         */
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            clearMeasureParams();
    
            int paddingLeft = getPaddingLeft();
            int paddingRight = getPaddingRight();
            int paddingTop = getPaddingTop();
            int paddingBottom = getPaddingBottom();
            //获取父控件的宽度高度
            int selfWidth = MeasureSpec.getSize(widthMeasureSpec);  //ViewGroup解析的父亲给我的宽度
            int selfHeight = MeasureSpec.getSize(heightMeasureSpec); // ViewGroup解析的父亲给我的高度
    
            //保存当前行所有view
            List<View> lineViews = new ArrayList<>();
            //当前行高度
            int lineHeight = 0;
            //当前行宽度
            int lineWidthUsed = 0;
    
    
            int parentNeededWidth = 0;  // measure过程中,子View要求的父ViewGroup的宽
            int parentNeededHeight = 0; // measure过程中,子View要求的父ViewGroup的高
    
    
            //获取所有孩子计算 递归计算孩子所需宽高
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                View childView = getChildAt(i);
                LayoutParams lp = childView.getLayoutParams();
                int childMeasureSpecWidth = getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight, lp.width);
                int childMeasureSpecHeight = getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom, lp.height);
                childView.measure(childMeasureSpecWidth, childMeasureSpecHeight);
    
                //当前view 的具体宽 高
                int childMeasuredWidth = childView.getMeasuredWidth();
                int childMeasuredHeight = childView.getMeasuredHeight();
    
    
                //判断是否需要换行
                if (lineWidthUsed + childMeasuredWidth + mHorizontalSpacing > selfWidth) {
                    //保存当前行 所有控件
                    allLineViews.add(lineViews);
                    //保存当前行高
                    lineHeights.add(lineHeight);
                    //记录 当前父控件所需宽高
                    parentNeededWidth = Math.max(parentNeededWidth, lineWidthUsed);
                    parentNeededHeight += lineHeight + mVerticalSpacing;
    
    
                    //当前行 相关保存数据 重置
                    lineViews = new ArrayList<>();
                    lineHeight = 0;
                    lineWidthUsed = 0;
                }
    
                //保存当前行所有控件
                lineViews.add(childView);
                //计算出最大高度//如每个控件高度不一样
                lineHeight = Math.max(lineHeight, childMeasuredHeight);
                //当前行宽度
                lineWidthUsed = lineWidthUsed + childMeasuredWidth + mHorizontalSpacing;
    
                if (i == childCount - 1) {//防止最后一个控件 是需要换行
                    //保存当前行 所有控件
                    allLineViews.add(lineViews);
                    //保存当前行高
                    lineHeights.add(lineHeight);
                    //记录 当前父控件所需宽高
                    parentNeededWidth = Math.max(parentNeededWidth, lineWidthUsed);
                    parentNeededHeight += lineHeight;
    
                }
            }
            //再度量自己,保存
            //根据子View的度量结果,来重新度量自己ViewGroup
            // 作为一个ViewGroup,它自己也是一个View,它的大小也需要根据它的父亲给它提供的宽高来度量
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    
            int realWidth = (widthMode == MeasureSpec.EXACTLY) ? selfWidth : parentNeededWidth;
            int realHeight = (heightMode == MeasureSpec.EXACTLY) ? selfHeight : parentNeededHeight;
            setMeasuredDimension(realWidth, realHeight);
        }
    
        /**
         * 确定 每个子view 的位置
         *
         * @param changed
         * @param l
         * @param t
         * @param r
         * @param b
         */
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            //确定第一个控件的xy位置 就是 paddingTop 和 paddingLeft w
            int curL = getPaddingLeft();
            int curT = getPaddingTop();
    
            int lineCount = allLineViews.size();
            for (int i = 0; i < lineCount; i++) {
                //获取所有行数 的控件
                List<View> views = allLineViews.get(i);
                //获取当前行高
                int height = lineHeights.get(i);
                //当前行数有几个控件
                int nowLienView = views.size();
                for (int j = 0; j < nowLienView; j++) {
                    //确定第一个控件位置
                    View view = views.get(j);
                    //调用过 onMeasure 这个就有值,
                    //右边位置 需要当前宽度 加上左边位置
                    int curR = view.getMeasuredWidth() + curL;
                    //下边位置 需要当前高度 加上上边位置
                    int curB = view.getMeasuredHeight() + curT;
                    view.layout(curL, curT, curR, curB);
                    //第二个或者之后的 x 轴需要改变也就是 curL 需要加上当前控件的宽 和 横向分割宽度
                    curL = curR + mHorizontalSpacing;
                }
                //第二行 left 需要重置,top 需要加上 上一行的高度 和 纵向分割宽度
                curL = getPaddingLeft();
                curT = curT + height + mVerticalSpacing;
            }
    
        }
    
    
        public int dp2px(float dpValue) {
            float scale = getContext().getResources().getDisplayMetrics().density;
            return (int) (dpValue * scale + 0.5f);
        }
    
    }
    
    
    

    相关文章

      网友评论

          本文标题:FlowLayout 自定义流式布局

          本文链接:https://www.haomeiwen.com/subject/mdxfhltx.html