使用FrameLayout進行自定義ViewGroup-測量和佈局策略

1、引言android

不少時候,當android系統控件不能知足咱們的業務需求時,咱們會考慮實現自定義view。而自定義View能夠分爲兩種狀況,一種是想實現View,另外一種是想實現ViewGroup。程序員

本文的介紹是自定義ViewGroup中所採用的一種常見方式是繼承FrameLayout來實現需求。佈局

2、FrameLayout的優點繼承

熟練掌握anroid佈局的程序員都知道,android系統提供了四種主要的佈局形式,LinearLayout、RelativeLayout、GridLayout、FrameLayout。開發

其中LinearLayout與GridLayout對於複寫不是很友好,由於它自己已經限定了佈局方式是特殊話的,很難再進行改造。而RelativeLayout與FrameLayout的佈局方式 相對的屬性較多,用戶的可控性大,同時,繼承以後的可塑性更強。get

兩種比較的話,RelativeLayout的實現更復雜一些,若是自己對其瞭解不夠深刻的話,可能會遇到不少的問題。FrameLayout的佈局理念和實現都相對簡單,同時又比ViewGroup多了具體的測量和佈局實現、。it

3、FrameLayout的測量策略io

1.FrameLayout的測量理念容器

所有子View都單獨測量,選擇最大的子View測量結果做爲FrameLayout的測量大小bug

2.FrameLayout的測量步驟

(1)關於MeasureSpec【來自父view的限制】

理解MeasureSpec是測量的首要要求,由於onMeasure的兩個參數就是widthSpec和HeightSpec。

下面介紹MeasureSpec的含義和做用:

a.measureSpec是一個int值,它的32位數據中,包含了兩種信息,mode和size。它的高兩位是mode,剩下30位是size。

其中mode可選的有三種 0 1 2分別是UNSPECIFIED、EXACTLY、AT_MOST。

UNSPECIFIED: 未指定,也就是沒有規則

EXACTLY:限制了大小,大小在size字段

AT_MOST:限制了最大的大小,大小在size字段

須要注意的是,提供的mode只是父view給當前view測量的一個參考和創建,你能夠聽從也能夠不聽從。

好比提供了mode爲EXACTLY,正常狀況下咱們會尊重父view的意見,設置本身爲對應的size(事實上默認view的測量結果也是這樣的,可見view的getDefaultSize方法).

可是你也能夠拒絕這種意見,設置本身的大小爲想要的,可是你可能也會付出代價(在layout的時候父view並不給你足夠的大小)。

因此,一切取決於你想作什麼。

(2)關於LayoutParams【來自子View的請求】

若是你是一個view,你關注meaurespec很大程度上就能解決問題,可是若是你是一個ViewGroup。你的子view的LayoutParams也會影響到你的測量(由於你是一個容器,你須要儘量的保證子view可以達到滿意的狀態)。

而LayoutParams就是子view在測量或佈局過程當中,所能提供給父view的請求信息,覺得父view爲子view的measureChild過程提供參考(一樣,父view也能夠忽略它)。

LayoutParams最重要也是不可缺的兩個屬性是layout_width和layout_height。

經驗豐富的開發者知道它能夠有三種選擇:
1. 一個肯定的大於0的dp值

2.MATCH_PARENT

3.WRAP_CONTENT
簡要說明:

一個肯定的大於0的dp值:即子view期待父佈局給本身這樣一個大小

MATCH_PARENT:子view不知道本身的大小,可是但願和父佈局同樣大

WRAP_CONTENT:子view也不知道本身的大小,它想測量本身完以後再作決定

此時,咱們想知道默認狀況下ViewGroup是如何測量的,

事實上,ViewGroup的onMeasure方法是空的,可是它提供了一個方法,這是它建議的測量策略-ViewGroup的getChildMeasureSpec方法。

這個方法並無決定ViewGroup的大小,而是肯定了子View的measureSpec,而後在測量完子view後能夠根據總體來決定ViewGroup自己的大小。

如今假設ViewGroup是須要測量的控件,parentMeasureSpec是父view提供的限定,即上面的(1),layoutparams是子View提供的建議。

這兩個每一個都有三種狀況,自由組合的話有九種可能性。讀者可自行閱讀這部分的代碼,比較容易理解。

(3)最終大小的設定

ViewGroup須要將測量結果肯定下來(由於父佈局會拿來決定本身的大小),因此須要有一個辦法可以設置本身的大小,

這個方法是setMeasuredDimension。這個方法須要在onMeasure裏面調用,因這個onMeasure方法執行完,可能會回到父view的測量方法中,它可能會很快用到。

一樣,父view只須要執行child.getMeasureWidth等...方法來獲取子view大小

(4)FrameLayout的解決方案

1.首先對全部的view都進行測量一遍,使用默認建議的測量方式,並考慮margin。(measurechildwithMargin) [若是你對layoutparams爲matchparent的child是如何測量的有疑問,能夠參考getChildMeasureSpec這個默認的處理方式]

2.找到layoutparams爲match_parent的child,放到一個list裏面。等非match_parent的測量完以後,注意此時framelayout的大小已經能夠肯定,由於全部有大小想法的view都已經測量完畢,FrameLayout能夠setMeasuredDimension了。

3.對於list裏面的child,須要進行再次測量,由於以前match_parent可是具體大小未定,如今定下來後,改變child的measurespec爲excatly,而後讓子view正確的測量。

(5) 爲何須要(4)中的3

須要對child再次測量,這是每一個viewGroup的責任,你應當至少須要對child進行一次測量(這樣子view能夠設置本身的measureWidth).這是一個健康的流程。

若是你不設置,那麼在接下來layout的時候便獲取不到子view的大小,那麼佈局沒有參考,此時會比較爲難,除非你肯定不須要子view的數據就能夠從新佈局它。

可是若是你的子view自己是viewGroup,那麼它若是沒有child的measureWidth,也很難走下去。

小結:測量的主要目的是讓各個view都瞭解並設置自身的大小,以便在接下來的layout過程當中可以提供恰當的參考,因此,不用作多,也不要不作

4、使用FrameLayout自定義ViewGroup在測量方面能夠作哪些操做

1.若是你不想某些view的測量結果影響FrameLayout的大小

事實上,你能夠將FrameLayout的onMeasure方法,在繼承的方法裏從新寫一遍,而後根據本身的邏輯調整,這是最簡單也是最容易改的方法。但有時候想盡可能減小寫代碼的數量(代碼多、bug就多).

2.不想拷貝代碼

那麼你只能經過繼承measureChildWIthMargin想一想辦法了,這樣可能會產生意想不到的bug。

5、FrameLayout的佈局策略

..待補充

相關文章
相關標籤/搜索