自定義ViewGroup 回顧

2021-09-11 15:29:47 字數 4228 閱讀 5033

自定viewgroup要比自定義view要複雜一點,因為自定義viewgroup不僅測量自身還要測量子元素和及重寫onlayout()來一次排列子view。下面這篇文章是關於自定義viewgroup的一些基本知識,這些主要內容來自《android開發藝術探索》,在文章最後又這本書的網上版本。

viewgroup是乙個抽象類,他沒有重寫view的onmeasure()方法。因此並沒有定義具體的測量過程,具體的測量過程交給了他的子類來完成,比如:linearlayoutrelativelayout等。viewgroup提供了乙個measurechildren的方法來測量子view:

protected void measurechildren(int widthmeasurespec, int heightmeasurespec) 

}}複製**

下面為measurechild()的原始碼:

protected void measurechild(view child, int parentwidthmeasurespec, int parentheightmeasurespec) 

複製**

measurechild()方法會先獲取子view的layoutparams引數,然後再通過getchildmeasurespec()獲取子view的寬高measurespec,最後將獲取到的measurespec傳遞給view的()方法進行測量。具體的執行過程我在《view的繪製流程》這篇文章中介紹過,這裡就不在多少說了。

onmeasure()方法

因為viewgroup沒有重寫view的onmeasure方法,我們在自定義的時候整合了viewgruppo成了view的子類,因此要寫自己布局的測量過則。那我上篇文章《自定義viewgroup—flowlayout》中的部分**為例:

@suppresslint("drawallocation")

@override

protected void onmeasure(int widthmeasurespec, int heightmeasurespec)

mlineview.addview(childview);

//已用寬度累加

usedwidth += childwidth;

mlineview.settotalwidth(usedwidth);

}//##3

for (int i = 0; i < mlineviewlist.size(); i++)

//父容器的總高度=上下padding的高度,在最後額外加乙個底部marginbottom

totalheight += getpaddingtop() + getpaddingbottom() + marginbottom;

//##4

setmeasureddimension(widthsize, heightmode == measurespec.exactly ? heightsize : totalheight);

}複製**

上面**中主要是測量flowlayout的高度。它是通過遍歷子view(//##1)計算剩餘寬度,再通過子view的寬度和剩餘寬度比較來判斷是否換行(//##2)。flowlayout的高度如果是在warp_cotent模式下高度就為子view的行數乘上子view的高度(//##3),最後通過setmeasureddimension()計算view的寬高並儲存起來。

onlayout()函式式是viewgroup的乙個抽象函式,viewgroup的子類必須實現該函式,用於定義view的擺放規則。

@override

protected abstract void onlayout(boolean changed,int l, int t, int r, int b);

複製**

該方法的呼叫是在view的layout被呼叫,我們可以檢視viewgroup的layout()放知道viewgroup的layout過程是交給父類完成的。

@override

public final void layout(int l, int t, int r, int b)

//呼叫父類的layout方法

super.layout(l, t, r, b);

} else

}複製**

view的layout方法中呼叫了onlayout()方法

@suppresswarnings()

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

...}複製**

對padding和margin的處理

padding的處理比較簡單,只需要getpadding***()來獲取padding的值,在計算viewgroup的寬高的時候將其加上即可:

paddingleft = getpaddingleft();

paddingtop = getpaddingtop();

paddingright = getpaddingright();

paddingbottom = getpaddingbottom();

//viewgroup寬高計算

viewgroupwidth = paddingleft + viewswidth + paddingright;

viewgroupheight = paddingtop + viewsheight + paddingbottom;

複製**

margin的處理比較麻煩一點,首先他要先從子view中獲取layoutparams屬性,通過子view的layoutparams屬性來獲取設定的margin值。其layoutparams獲取方法為childview.getlayoutparams()。要注意下面兩點:

獲取的要是marginlayoutparams()型別,記得做型別強轉。

重新generatelayoutparams(),返回型別為marginlayoutparams類或他的子類。否則回報型別轉換異常

下面為實現**:

@suppresslint("drawallocation")

@override

protected void onmeasure(int widthmeasurespec, int heightmeasurespec)

//重寫generatelayoutparams()

@override

public layoutparams generatelayoutparams(attributeset attrs)

複製**

android中不能再activity的生命週期oncreate()onstart()onresume()生命週期中獲取到view的寬高,這是因為activity的生命週期和view的測量過程不是同步執行的。對於上面的問題有四種解決方案。下面為三種解決方法,第四種方案比較複雜就沒寫出來。

onwindowfoucuschanged()中獲取

@override

public void onwindowfocuschanged(boolean hasfocus)

複製**

通過view的post方法,經請求傳送到訊息佇列中執行。
mflowl_testcustom.post(new runnable

() });

複製**

通過為viewtreeobserver新增ongloballayoutlistener()來實現
viewtreeobserver treeobserver=mflowl_testcustom.getviewtreeobserver();

treeobserver.addongloballayoutlistener(new viewtreeobserver.ongloballayoutlistener

() });

複製**

文章先寫到這裡吧!最近一直在堅持每天寫技術筆記,希望能慢慢將這種堅持當成一種習慣。最後祝所有看到這篇文章的人工作順利,工資翻番。

android開發藝術探索完結篇——天道酬勤

自定義ViewGroup(一)

1 概述 viewgroup是乙個view的容器,他可以給出childview的測量模式和測量其寬高,他的作用非常重要。childview測量模式 exactly 表示設定了精確的值,一般當childview設定其寬 高為精確值 match parent時,viewgroup會將其設定為exactl...

ViewGroup 自定義控制項

自定義viewgroup這篇文章是針對自定義layoutmanager來寫的,提取出相關自定義的相同點。所有的自定義都可以歸結為在父控制項裡面放置子控制項。一 繼承類 viewgroup 繼承之後需要實現構造,由於一般是在xml中引入所有需要實現以下構造 viewgroup context cont...

自定義ViewGroup及其屬性

閒來無事自定義個viewgroup的控制項來練練手。比如說現在有這麼個需求,一左一右分別有個textview,然後外面乙個控制項直接包裹這兩個 1 現在給這個自定義控制項 本文中名叫rclinearlayout 自定義乙個屬性,然後去通過這個屬性去確定這兩個textview是否需要處於同一水平線上。...