自定义View---FlowLayout

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());

    }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,585评论 4 365
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,923评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,314评论 0 248
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,346评论 0 214
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,718评论 3 291
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,828评论 1 223
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,020评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,758评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,486评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,722评论 2 251
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,196评论 1 262
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,546评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,211评论 3 240
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,132评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,916评论 0 200
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,904评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,758评论 2 274

推荐阅读更多精彩内容