自定viewgroup要比自定義view要複雜一點,因為自定義viewgroup不僅測量自身還要測量子元素和及重寫onlayout()來一次排列子view。下面這篇文章是關於自定義viewgroup的一些基本知識,這些主要內容來自《android開發藝術探索》,在文章最後又這本書的網上版本。
viewgroup是乙個抽象類,他沒有重寫view的onmeasure()方法。因此並沒有定義具體的測量過程,具體的測量過程交給了他的子類來完成,比如:linearlayout
、relativelayout
等。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是否需要處於同一水平線上。...