技術(shù)的進(jìn)步,總是由需求推動(dòng)的 ——安卓君
1、前言
流式布局是app開發(fā)中必不可少的布局方式,例如照片墻,篩選標(biāo)簽等等?;旧厦總€(gè)app里面都可以看到這樣的布局,但是android API中并沒有提供實(shí)現(xiàn)流式布局方式的控件,因此自己實(shí)現(xiàn)一個(gè)流式布局的控件就非常有必要了。本文分享筆者實(shí)現(xiàn)FlowLayout思考過程,希望能夠拋轉(zhuǎn)引玉,為大家提供一個(gè)新的思路,文章末尾有代碼鏈接。
2、效果
下面是效果圖:

3、分析
實(shí)現(xiàn)自定義控件肯定要從View的繪制原理開始思考,關(guān)于View的繪制原理這里就不做介紹了。首先可以確定的是我們要實(shí)現(xiàn)的是一個(gè)ViewGroup,多個(gè)標(biāo)簽(子控件(View)) 被放置到ViewGroup中。所以要實(shí)現(xiàn)的有:
-
1.測(cè)量父控件(ViewGroup)的大小
-
2.放置子控件(View)
體現(xiàn)在代碼中就是實(shí)現(xiàn)父控件的兩個(gè)方法:
-
1.onMeasure()
-
2.onLayout()
3.1、計(jì)算父控件的寬高
3.1.1 計(jì)算父控件寬度
流式布局寬度一般都是指定的,match_parent 或者具體數(shù)值。測(cè)量模式為MeasureSpec.EXACTLY,可以通過MeasureSpec.getSize(widthMeasureSpec);方法獲取。如果不是指定寬度,就無法換行擺放子控件,流式布局也就不存在了。
3.1.2 計(jì)算父控件的高度
結(jié)合效果圖看,父控件的高度由子控件行數(shù)決定的,本文假設(shè)每個(gè)子控件高度一致,行間距一致,間隔一致。
父控件高度 = paddingTop + paddingBottom + 行高*行數(shù) + 行間距*(行數(shù) - 1)

可以看到計(jì)算高度的關(guān)鍵是,所以剩下的問題就是如何計(jì)算行數(shù)。
如何計(jì)算行數(shù)?
計(jì)算行數(shù)的關(guān)鍵在于知道什么時(shí)候換行,我們先看看效果圖每一行子控件實(shí)際占據(jù)的寬度

在FlowLayout中,一行子控件實(shí)際占據(jù)寬度為
行寬度 = 子控件寬度 + 間隔 ... (間隔數(shù)比子控件少一個(gè))
所以我們可以很容易得出這樣一個(gè)換行條件
paddingLeft + 行寬度 + 下一個(gè)子控件的寬度 + paddingRight > 父控件寬度
我們可以通過for循環(huán)遍歷子控件集合計(jì)算出總行數(shù),當(dāng)我們得出行數(shù)時(shí),就可以計(jì)算出父控件的高度,測(cè)量寬高的工作就完成了。
3.2 放置子控件
放置子控件,最終調(diào)用
layout(int l, int t, int r, int b)
所以只要得到每個(gè)子控件的位置信息就可以最終展現(xiàn)出流式布局,很多人在實(shí)現(xiàn)FlowLayout時(shí),會(huì)在這里重新測(cè)量再計(jì)算子控件的位置,我覺得比較繁瑣,而且重復(fù)的測(cè)量也耗費(fèi)資源。我想到,在onMeasure()方法中,需要遍歷子控件計(jì)算寬高,那么為什么不在遍歷的時(shí)候,計(jì)算出每個(gè)子控件的位置,再通過setTag()方法把位置信息賦值給子控件呢?那樣的話在執(zhí)行onLayout()方法,放置子控件的時(shí)候,就可以通過遍歷子控件,getTag() 得到每個(gè)子控件的位置信息,就可以實(shí)現(xiàn)所有子控件的放置了。
如何計(jì)算每個(gè)子控件的位置?

如上圖所示,只要計(jì)算出子控件的寬高,我們很容易就能得出left,top,bottom,right值。
4.實(shí)現(xiàn)單選和多選
如何實(shí)現(xiàn)子控件的單選功能?
可以借鑒RadioGroup實(shí)現(xiàn)原理,也可以換一種思路,直接繼承RadioGroup就可以實(shí)現(xiàn)單選的功能,添加RadioButton作為子控件,相當(dāng)于把RadioGroup改造成具有流式布局功能的控件。
如何實(shí)現(xiàn)子控件的多選功能?
如果添加的子控件都是CheckBox,就可以實(shí)現(xiàn)多選的功能。
5.總結(jié)
FlowLayout算不上非常復(fù)雜的控件,原理也很簡(jiǎn)單,一個(gè)是計(jì)算父容器的寬高,一個(gè)是獲取子控件的位置。本文從筆者實(shí)際業(yè)務(wù)出發(fā),行間距,間隔都是在自定義屬性中設(shè)置的固定值,實(shí)現(xiàn)起來也簡(jiǎn)單。自認(rèn)為本文特別之處在于,提供新的思路,讓流式布局實(shí)現(xiàn)起來更簡(jiǎn)單優(yōu)雅
在測(cè)量子控件時(shí),計(jì)算每個(gè)控件的位置,并設(shè)置到子控件
本文主要分享思考過程、實(shí)現(xiàn)方法,不能說多完美,希望能帶給大家一點(diǎn)啟發(fā)。作者歡迎評(píng)論,探討!
源碼地址:https://github.com/f1mert/SimpleFlowLayout 歡迎點(diǎn)擊!