超简单android自定义控件流式布局SimpleFlowLayout

技术的进步,总是由需求推动的 ——安卓君

1、前言

流式布局是app开发中必不可少的布局方式,例如照片墙,筛选标签等等。基本上每个app里面都可以看到这样的布局,但是android API中并没有提供实现流式布局方式的控件,因此自己实现一个流式布局的控件就非常有必要了。本文分享笔者实现FlowLayout思考过程,希望能够抛转引玉,为大家提供一个新的思路,文章末尾有代码链接。

2、效果

下面是效果图:

SimpleFlowLayout示例图

3、分析

实现自定义控件肯定要从View的绘制原理开始思考,关于View的绘制原理这里就不做介绍了。首先可以确定的是我们要实现的是一个ViewGroup,多个标签(子控件(View)) 被放置到ViewGroup中。所以要实现的有:

  • 1.测量父控件(ViewGroup)的大小
  • 2.放置子控件(View)

体现在代码中就是实现父控件的两个方法:

  • 1.onMeasure()
  • 2.onLayout()

3.1、计算父控件的宽高

3.1.1 计算父控件宽度

流式布局宽度一般都是指定的,match_parent 或者具体数值。测量模式为MeasureSpec.EXACTLY,可以通过MeasureSpec.getSize(widthMeasureSpec);方法获取。如果不是指定宽度,就无法换行摆放子控件,流式布局也就不存在了。

3.1.2 计算父控件的高度

结合效果图看,父控件的高度由子控件行数决定的,本文假设每个子控件高度一致,行间距一致,间隔一致。

父控件高度 = paddingTop + paddingBottom + 行高*行数 + 行间距*(行数 - 1)
计算高度

可以看到计算高度的关键是,所以剩下的问题就是如何计算行数。

如何计算行数?

计算行数的关键在于知道什么时候换行,我们先看看效果图每一行子控件实际占据的宽度


计算宽度

在FlowLayout中,一行子控件实际占据宽度为

行宽度 = 子控件宽度 + 间隔 ... (间隔数比子控件少一个)

所以我们可以很容易得出这样一个换行条件

paddingLeft + 行宽度 +  下一个子控件的宽度 + paddingRight > 父控件宽度

我们可以通过for循环遍历子控件集合计算出总行数,当我们得出行数时,就可以计算出父控件的高度,测量宽高的工作就完成了。

3.2 放置子控件

放置子控件,最终调用

layout(int l, int t, int r, int b)

所以只要得到每个子控件的位置信息就可以最终展现出流式布局,很多人在实现FlowLayout时,会在这里重新测量再计算子控件的位置,我觉得比较繁琐,而且重复的测量也耗费资源。我想到,在onMeasure()方法中,需要遍历子控件计算宽高,那么为什么不在遍历的时候,计算出每个子控件的位置,再通过setTag()方法把位置信息赋值给子控件呢?那样的话在执行onLayout()方法,放置子控件的时候,就可以通过遍历子控件,getTag() 得到每个子控件的位置信息,就可以实现所有子控件的放置了。

如何计算每个子控件的位置?

计算子控件位置

如上图所示,只要计算出子控件的宽高,我们很容易就能得出left,top,bottom,right值。

4.实现单选和多选

如何实现子控件的单选功能?

可以借鉴RadioGroup实现原理,也可以换一种思路,直接继承RadioGroup就可以实现单选的功能,添加RadioButton作为子控件,相当于把RadioGroup改造成具有流式布局功能的控件。

如何实现子控件的多选功能?

如果添加的子控件都是CheckBox,就可以实现多选的功能。

5.总结

FlowLayout算不上非常复杂的控件,原理也很简单,一个是计算父容器的宽高,一个是获取子控件的位置。本文从笔者实际业务出发,行间距,间隔都是在自定义属性中设置的固定值,实现起来也简单。自认为本文特别之处在于,提供新的思路,让流式布局实现起来更简单优雅

在测量子控件时,计算每个控件的位置,并设置到子控件

本文主要分享思考过程、实现方法,不能说多完美,希望能带给大家一点启发。作者欢迎评论,探讨!
源码地址:https://github.com/f1mert/SimpleFlowLayout 欢迎点击!

你可能感兴趣的:(超简单android自定义控件流式布局SimpleFlowLayout)