本文探究測量的全過程,不是簡單的介紹onMeasure()函數!
一、測量的目的
如果安卓的佈局體系中全部都是精確的值,那就沒有必要關心測量過程了。事實上,在佈局文件中寫的佈局參數都是match_parent,wrap_parent或者精確值。
測量的目的就是爲了將match_parent,wrap_parent這些相對大小,轉換爲具體的值。
二、測量的依據
應用開發者有三種方式表示長寬:match_parent,wrap_parent,精確值
測量體系內部有三種模式:EXACTILY,AT_MOST,UNSPECIFIED
其轉換所對應的表格如下:
paretMeasureSpec |
Lp.height |
childMeasureSpec |
EXACTLY+SIZE |
具體值 |
EXACTLY+具體值 |
同上 |
WRAP_CONTENT |
AT_MOST+size |
同上 |
MATCH_PARENT |
EXACTLY+size |
AT_MOST+SIZE |
具體值 |
EXACTLY+size |
同上 |
WRAP_CONTENT |
AT_MOST+size |
同上 |
MATCH_PARENT |
AT_MOST+size |
UNSPECIFIED+SIZE |
childDimension |
EXACTLY+childDimension |
同上 |
WRAP_CONTENT |
UNSPECIFIED+0 |
同上 |
MATCH_PARENT |
UNSPECIFIED+0 |
測量規格即表中的MeasureSpec,其組成爲:測量模式+大小。
測量模式有三種:
EXACTILIY:父視圖期待子視圖的最理想大小
AT_MOST:父視圖允許子視圖的最大值
UNSEPECFIED:好吧,我不知道這個做啥用的,基本沒遇到過
上述表格意義解釋:
paretMeasureSpec:這是測量體系傳遞下來的,由系統轉換完成
Lp.height:對應XML文件中latyou_height = "XXXX",這裏是由程序猿指定。
ChildMeasureSpec :經過表的轉換後得出自己的測量模式和測量中值,如果當前測量控件是一個ViewGroup,這個值還會傳遞給子控件。
三、測量過程
整個的測量過程,根據個人的理解做了一個圖,將就着看:
將視圖分爲兩類:View和ViewGroup
1-> ViewGroup有兩個動作:
動作一:measure(父控件給定的寬規格,父控件給定的高規格) 函數接受來自父控件指定的測量規格,然後結合自己的佈局參數 (layout_width,layou_height)來測量自身的寬和高 ,測量模式。這個時候就用到了最上面的表格,這一步的時候,measure中會回調onMeasure();
這就是View中的onMeasure()函數中的參數是怎麼來的了。
動作二:
根據padding等屬性,綜合進行考慮,給每個子控件分配測量規格
2-> View相對簡單,只有一個操作:
只有動作一:根據父控件傳遞下來的寬高測量規格,得出自己的寬高。同樣的也會回調onMeasure()方法。
在實際的使用過程,一遍的控件都有父控件,有父控件提供測量規格進行參考,那根視圖呢?
根視圖有單獨的計算方法,其大小需要結合窗口的佈局參數。一般情況,根視圖的measure方法所獲得測量規格爲Exactly+窗口大小!
注意:
以上測量的過程都是系統完成,只有onMeasure()這一步應用程序可以參與。而且各種規格都只是建議,應用程序的代碼可以通過 setMeasureDimension()來一票否決,直接指定當前控件的大小。
同時,不建議直接繼承ViewGroup,通過上述的過程,應該知道,如果直接繼承ViewGroup你就需要處理給子空間分配大小的過程,這個過程肯定麻煩。
大多數的開源控件中也很有直接繼承ViewGroup,要麼是繼承的View,要麼就是繼承已有的VIewGroup子類,像LinearLayout之類的。
四、實際的運用過程。
一個窗口承載一個視圖,視圖最終會表現爲一個View構成的樹。每個節點(也就是View)必須擁有LayoutParam的屬性。
來看看View的獲得方式:
方式一:new 一個View
方式二:通過 LayoutInflater.from(this).inflate(R.layout.XXX,null);的方式來構造出一個View
如果你調用getLayoutParams(),會發現它們的佈局參數都是空的。
方式一不奇怪,你本來就沒有設置佈局參數,自然沒有
方式二奇怪的很,不是佈局XML裏面一般都有寫麼!其實是構造的過程最外層的ViewGroup的寬高屬性是沒有生效的,如果需要使得它生效,需要調用LayoutInfalter.from(this).infate的另外一種用法。
既然沒有佈局參數,它們最後都是怎麼測量的?
其實,以上的view都是孤立的View,如果需要讓它可見,一定要把通過addView的方法,加入主幹的視圖樹中。查看addView這個方法會發現有個特點,如果你加入的View沒有佈局參數,它會生成一個默認的參數。如果有就使用子控件的佈局參數。
這裏有一點需要注意到 是,如果給一個LinearLayout通過addView的方式添加一個View,而這個View你通過setLayoutParams的方式指定了一個佈局參數,這個指定的佈局參數必須是LinearLayout.LayoutParams ,不然就會報類型錯誤。
如下圖所示,每個VIew所對應的LayoutParams類型是是父母控件的類型的佈局參數!
實際的運用就是,需要對控件本身定製大小,不受父母控件的干擾,就手動的設置精確佈局參數,需要注意其類型,類型一旦確實就只能添加到某個指定的控件下了。