美文网首页
自定义View---FlowLayout

自定义View---FlowLayout

作者: 剧透下 | 来源:发表于2021-07-20 08:46 被阅读0次

    import android.content.Context;

    import android.content.res.Resources;

    import android.util.AttributeSet;

    import android.util.TypedValue;

    import android.view.View;

    import android.view.ViewGroup;

    import java.util.ArrayList;

    import java.util.List;

    /**

    * @ProjectName: MyKotlinSample

    * @Package: com.example.mykotlinsample

    * @ClassName: TestFlowLayout

    * @Description: java类作用描述

    * @Author: Zcb

    * @CreateDate: 2021/7/19 16:34

    * @UpdateRemark: 更新说明

    */

    public class TestFlowLayoutextends ViewGroup {

    private List>allLines =new ArrayList<>(); // 记录所有的行,一行一行的存储,用于layout

        ListlineHeights =new ArrayList<>(); // 记录每一行的行高,用于layout

        private int mHorizontalSpacing =dp2px(16); //每个item横向间距

        private int mVerticalSpacing =dp2px(8); //每个item横向间距

        public TestFlowLayout(Context context) {

    super(context);

        }

    public TestFlowLayout(Context context, AttributeSet attrs) {

    super(context, attrs);

        }

    public TestFlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {

    super(context, attrs, defStyleAttr);

        }

    /**

        * 对UI布局进行测量

        *

        * @param widthMeasureSpec  是当前类TestFlowLayout的父布局所测量后的数值(xml中 TestFlowLayout 的父布局)

        * @param heightMeasureSpec 同理

        */

        @Override

        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    allLines.clear();

            lineHeights.clear();

            //todo  首先测量子View,然后将子View的宽高交由父类ViewGroup,最后在测量父类View本身

            // 第一步 先测量子View本身

            //获取在ViewGroup中的子View

            int childCount = getChildCount();

            int paddingLeft = getPaddingLeft();

            int paddingRight = getPaddingRight();

            int paddingTop = getPaddingTop();

            int paddingBottom = getPaddingBottom();

            List lineViews =new ArrayList<>(); //保存一行中的所有的view

            int lineWidthUsed =0; //记录当前行已经使用了多宽的size

            int lineHeightUsed =0; // 记录当前一行的行高

            int selfWidth = MeasureSpec.getSize(widthMeasureSpec);  //ViewGroup解析的父亲给我的宽度

            int selfHeight = MeasureSpec.getSize(heightMeasureSpec); // ViewGroup解析的父亲给我的高度

            int parentNeededWidth =0;  // measure过程中,子View要求的父ViewGroup的宽

            int parentNeededHeight =0; // measure过程中,子View要求的父ViewGroup的高

            //遍历所有子view

            for (int i =0; i < childCount; i++) {

    View childView = getChildAt(i);//获取具体的某个子View

                LayoutParams chideLp = childView.getLayoutParams();//获取子View的属性

                int height = chideLp.height;

                int width = chideLp.width;

                /**

                * 根据不同的模式来设置MeasureSpec

    *

                * 想要获取子View的宽度,必须在父类中进行,在由不同的模式下specMode来对子类进行一个测量具体值并返回MeasureSpec,

                *

                * 可查看源码 了解子View的宽高属性的由来

                */

                int childWidthMeasureSpec =getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight, width);//得到一个MeasureSpec宽

                int childHeightMeasureSpec =getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom, height);//得到一个MeasureSpec高

                //再由子View对宽高进行一个测量得出具体的数值, - 相当于保存当前子View信息,并不是真正意义上子View的宽高

                //该值为父类提供宽度和高度参数中的约束信息 //源码解释

                //得到宽高后,确定在父容器的位置?

                childView.measure(childWidthMeasureSpec, childHeightMeasureSpec);

                lineViews.add(childView);// childView 是分行layout的,所以要记录每一行有哪些view,这样可以方便layout布局

                //获取子view的度量宽高

                int childMesauredWidth = childView.getMeasuredWidth();

                int childMesauredHeight = childView.getMeasuredHeight();

                //此时已经得到每个具体的子View ,则要进行计算一行最多能放几个View

                lineWidthUsed = childMesauredWidth + lineWidthUsed +mHorizontalSpacing;

                lineHeightUsed = Math.max(lineHeightUsed, childMesauredHeight);//获取该行中 最高的子View

                //问题来了,怎么换行呢?

                //如果需要换行, ----> 将当前的子View与已占用的空间,再加上间距 == 一行的宽度, 但是不能大于父容器的宽度,so...

                if (childMesauredWidth + lineWidthUsed +mHorizontalSpacing > selfWidth) {

    //一旦换行,我们就可以判断当前行需要的宽和高了,所以此时要记录下来

                    allLines.add(lineViews);

                    lineHeights.add(lineHeightUsed);

                    parentNeededHeight = parentNeededHeight + lineHeightUsed +mVerticalSpacing;

                    parentNeededWidth = parentNeededWidth + lineWidthUsed +mHorizontalSpacing;

                    //换行后,将记录的 View宽高 都重置

                    lineViews =new ArrayList<>();

                    lineWidthUsed =0;

                    lineHeightUsed =0;

                }

    //处理最后一行数据

                if (i == childCount -1) {

    allLines.add(lineViews);

                    lineHeights.add(lineHeightUsed);

                    parentNeededHeight = parentNeededHeight + lineHeightUsed +mVerticalSpacing;

                    parentNeededWidth = Math.max(parentNeededWidth, lineWidthUsed +mHorizontalSpacing);

                }

    }

    //TODO 测量完子View本身后, 测量父类自己Viewgroup

            //再度量自己,保存

            //根据子View的度量结果,来重新度量自己ViewGroup

            // 作为一个ViewGroup,它自己也是一个View,它的大小也需要根据它的父亲给它提供的宽高来度量

            int widthMode = MeasureSpec.getMode(widthMeasureSpec);

            int heightMode = MeasureSpec.getMode(heightMeasureSpec);

            //MeasureSpec.EXACTLY 得到的是精准的数值

            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) {

    int lineCount =allLines.size();//获取所有行数

            int curL = getPaddingLeft();//获取子View距离该子View背景左边的距离

            int curT = getPaddingTop();//同理

            //todo 双for循环遍历 外层for循环总行数,内层for获取每一行的子View

            for (int i =0; i < lineCount; i++) {//遍历每一行

                List lineViews =allLines.get(i);//获取 记录每一行的子View

                int lineHeight =lineHeights.get(i);//获取记录每一行的高度

                for (int j =0; j < lineViews.size(); j++) {//遍历每一行的子view

                    View view = lineViews.get(j);//得到具体的子View

                    int left = curL;

                    int top = curT;

    //                int right = left + view.getWidth();//此方法是在onLayout方法之后赋值

    //                int bottom = top + view.getHeight();

                    /**

                    * view.getMeasuredWidth() 则是获取当前子View的具体宽度

                    *

                    * 此方法是在Measured方法之后赋值

                    *

    */

                    int right = left + view.getMeasuredWidth();

                    int bottom = top + view.getMeasuredHeight();

                    view.layout(left, top, right, bottom);//确定子View的位置

                    curL = right +mHorizontalSpacing;// 下一个子View的位置

                }

    curT = curT + lineHeight +mVerticalSpacing;

                curL = getPaddingLeft();

            }

    }

    public static int dp2px(int dp) {

    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics());

        }

    相关文章

      网友评论

          本文标题:自定义View---FlowLayout

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