Android進(jìn)階教程之ViewGroup自定義布局
前言
在我們的實(shí)際應(yīng)用中, 經(jīng)常需要用到自定義控件,比如自定義圓形頭像,自定義計(jì)步器等等。但有時(shí)我們不僅需要自定義控件,舉個(gè)例子,F(xiàn)loatingActionButton 大家都很常用,所以大家也很經(jīng)常會(huì)有一種需求,點(diǎn)擊某個(gè) FloatingActionButton 彈出更多 FloatingActionButton ,這個(gè)需求的一般思路是寫(xiě) n 個(gè) button 然后再一個(gè)個(gè)的去設(shè)置動(dòng)畫(huà)效果。但這實(shí)在是太麻煩了,所以網(wǎng)上有個(gè) FloatingActionButtonMenu 這個(gè)開(kāi)源庫(kù),這就是利用到了自定義布局 「ViewGroup」,現(xiàn)在就讓我給他家介紹下,如何自定義布局 「layout」。

難點(diǎn)
相比于自定義 View ,自定義 ViewGroup 的難點(diǎn)在于,子控件位置的確定和布局大小的確定。不像 單個(gè) View 子要花粉好模式,測(cè)量好寬度就搞定了,ViewGroup 的長(zhǎng)寬根據(jù)子 View 的數(shù)量和單個(gè)的大小變化而變化。這就是最大的坎,所以該如何確定 ViewGroup 的大小呢?
步驟
這里 我為大家設(shè)計(jì)一個(gè) 類似 LinearLayout 線性布局的 ViewGroup 作為范例。
首先,如果是一個(gè) LinearLayout 那么當(dāng)設(shè)置 wrap_content 時(shí),他就會(huì)以子空間中最寬的那個(gè)為它的寬度。同時(shí)在高度方面會(huì)是所有子控件高度的總和。所以我們先寫(xiě)兩個(gè)方法,分別用于測(cè)量 ViewGroup 的寬度和高度。
private int getMaxWidth(){
int count = getChildCount();
int maxWidth = 0;
for (int i = 0 ; i < count ; i ++){
int currentWidth = getChildAt(i).getMeasuredWidth();
if (maxWidth < currentWidth){
maxWidth = currentWidth;
}
}
return maxWidth;
}
private int getTotalHeight(){
int count = getChildCount();
int totalHeight = 0;
for (int i = 0 ; i < count ; i++){
totalHeight += getChildAt(i).getMeasuredHeight();
}
return totalHeight;
}
對(duì)于 ViewGroup 而言我們可以粗略的分為兩種模式:固定長(zhǎng)寬模式(match_parent),自適應(yīng)模式(wrap_content),根據(jù)這兩種模式,就可以對(duì) ViewGroup 的繪制進(jìn)行劃分。這里關(guān)于 measureChildren 這個(gè)方法,他是用于將所有的子 View 進(jìn)行測(cè)量,這會(huì)觸發(fā)每個(gè)子 View 的 onMeasure 函數(shù),但是大家要注意要與 measureChild 區(qū)分,measureChild 是對(duì)單個(gè) view 進(jìn)行測(cè)量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int heightMode= MeasureSpec.getMode(heightMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST){
int groupWidth = getMaxWidth();
int groupHeight= getTotalHeight();
setMeasuredDimension(groupWidth, groupHeight);
}else if (widthMode == MeasureSpec.AT_MOST){
setMeasuredDimension(getMaxWidth(), height);
}else if (heightMode == MeasureSpec.AT_MOST){
setMeasuredDimension(width, getTotalHeight());
}
}
重寫(xiě) onLayout
整完上面這些東西,我們的布局大小七十九已經(jīng)出來(lái)了,然我們?cè)诨顒?dòng)的布局文件里面加上它,并添加上幾個(gè)子 View 然后運(yùn)行一下,先看看效果:
<com.entry.android_view_user_defined_first.views.MyLinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/colorAccent"> <Button android:layout_width="100dp" android:layout_height="50dp" android:text="qwe"/> <Button android:layout_width="250dp" android:layout_height="150dp" android:text="qwe"/> <Button android:layout_width="200dp" android:layout_height="75dp" android:text="qwe"/> </com.entry.android_view_user_defined_first.views.MyLinearLayout>
運(yùn)行效果如下:

我們看見(jiàn)布局出來(lái)了,大小好像也沒(méi)啥問(wèn)題,但是子 View 呢??! 這么沒(méi)看見(jiàn)子 View 在看看代碼,系統(tǒng)之前然我們重寫(xiě)的 onLayout() 還是空著的呀!!也就是說(shuō),子 View 的大小和位置根本就還沒(méi)有進(jìn)行過(guò)設(shè)定!讓我們來(lái)重寫(xiě)下 onLayout() 方法。
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int count = getChildCount();
int currentHeight = 0;
for (int i = 0 ; i < count ; i++){
View view = getChildAt(i);
int height = view.getMeasuredHeight();
int width = view.getMeasuredWidth();
view.layout(l, currentHeight, l + width, currentHeight + height);
currentHeight += height;
}
}
再運(yùn)行一下看看:

成功了有木有!
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
Android List(集合)中的對(duì)象以某一個(gè)字段排序案例
這篇文章主要介紹了Android List(集合)中的對(duì)象以某一個(gè)字段排序案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08
Android開(kāi)發(fā)之TabActivity用法實(shí)例詳解
這篇文章主要介紹了Android開(kāi)發(fā)之TabActivity用法,結(jié)合實(shí)例形式較為詳細(xì)的分析了Android擴(kuò)展Activity實(shí)現(xiàn)標(biāo)簽頁(yè)效果的具體步驟與相關(guān)技巧,需要的朋友可以參考下2016-03-03
Android?webView加載數(shù)據(jù)時(shí)內(nèi)存溢出問(wèn)題及解決
這篇文章主要介紹了Android?webView加載數(shù)據(jù)時(shí)內(nèi)存溢出問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
Android拆輪子系列之寫(xiě)驗(yàn)證碼控件的方法
這篇文章主要介紹了Android拆輪子系列之寫(xiě)驗(yàn)證碼控件的方法的, 涉及到Canvas和pint的使用和View的基本用法等相關(guān)知識(shí)點(diǎn),本文給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2016-10-10
Android應(yīng)用動(dòng)態(tài)修改主題的方法示例
今天小編就為大家分享一篇關(guān)于Android應(yīng)用動(dòng)態(tài)修改主題的方法示例,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03
Android App安裝列表獲取方法(實(shí)踐方案)
文章介紹了Android 11及以上版本獲取應(yīng)用列表的方案調(diào)整,包括權(quán)限配置、白名單配置和action配置三種方式,并提供了相應(yīng)的Java和Kotlin代碼示例,建議在Android 15及以上版本中使用action方式獲取應(yīng)用列表,感興趣的朋友一起看看吧2025-03-03
移動(dòng)端html5圖片上傳方法【更好的兼容安卓IOS和微信】
這篇文章主要為大家詳細(xì)介紹了移動(dòng)端html5圖片上傳方法,更好的兼容安卓IOS和微信,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-06-06
Android實(shí)現(xiàn)界面定時(shí)刷新功能
在移動(dòng)應(yīng)用中,界面定時(shí)刷新是非常常見(jiàn)的需求,本教程將以一個(gè)“實(shí)時(shí)時(shí)鐘”示例為主線,演示多種常用的定時(shí)刷新的實(shí)現(xiàn)方式,并對(duì)比它們的代碼簡(jiǎn)潔度、性能消耗、生命周期管理與取消機(jī)制,幫助您在項(xiàng)目中快速選型并上手,需要的朋友可以參考下2025-04-04
android中圖片的三級(jí)緩存cache策略(內(nèi)存/文件/網(wǎng)絡(luò))
實(shí)現(xiàn)圖片緩存也不難,需要有相應(yīng)的cache策略。這里我采用 內(nèi)存-文件-網(wǎng)絡(luò) 三層cache機(jī)制,其中內(nèi)存緩存包括強(qiáng)引用緩存和軟引用緩存(SoftReference),其實(shí)網(wǎng)絡(luò)不算cache,這里姑且也把它劃到緩存的層次結(jié)構(gòu)中2013-06-06

