MeasureSpec
View的LayoutParams根據(jù)父容器所施加的規(guī)則轉(zhuǎn)換成對應(yīng)的MeasureSpec,然后根據(jù)這個MeasureSpec來測量出View的寬高。MeasureSpec代表一個32位int值,高2位代表SpecMode(測量模式),低30位代表SpecSize(在某個測量模式下的規(guī)格大小)
//獲取SpecSize:
int specSize = MeasureSpec.getSize(measureSpec)
//獲取specMode
int measureSpec=MeasureSpec.makeMeasureSpec(size, mode);
SpecMode一共有三種:
UNSPECIFIED :父容器不對View進行任何限制,要多大給多大,一般用于系統(tǒng)內(nèi)部
EXACTLY:父容器檢測到View所需要的精確大小,這時候View的最終大小就是SpecSize所指定的值,對應(yīng)LayoutParams中的match_parent和具體數(shù)值這兩種模式
AT_MOST :對應(yīng)View的默認大小,不同View實現(xiàn)不同,View的大小不能大于父容器的SpecSize,對應(yīng)LayoutParams中的wrap_content
MeasureSpec和LayoutParams的對應(yīng)關(guān)系
View的MeasureSpec由父容器的MeasureSpec和自身的LayoutParams共同決定。
對于普通View來說,View的measure過程由ViewGroup傳遞過來,measureChildWithMargins源碼
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
measureChildWithMargins()方法對子元素進行measure,在調(diào)用子元素的measure方法之前先通過
measureChildWithMargins()方法得到子元素的MeasureSpec,子元素的MeasureSpec與父容器的MeasureSpec和自身的LayoutParams有關(guān),還與view的margin和padding有關(guān)。
View 的MeasureSpec創(chuàng)建規(guī)則(getChildMeasureSpec方法的表格呈現(xiàn))

- 當(dāng)View采用固定寬/高時(即設(shè)置固定的dp/px),不管父容器的MeasureSpec是什么,View的MeasureSpec都是EXACTLY模式,并且大小遵循我們設(shè)置的值。
- 當(dāng)View的寬/高是match_parents時,如果父容器的模式是精準模式,那么View也是精準模式并且其大小是父容器的剩余空間;如果父容器是最大模式那么View也是最大模式并且其大小不會超過父容器的剩余空間
- 當(dāng)View的寬/高是wrap_content時,View的MeasureSpec都是AT_MOST模式并且其大小不能超過父容器的剩余空間。
只要提供父容器的MeasureSpec和子元素的LayoutParams,就可以確定出子元素的MeasureSpec,進一步便可以確定出測量后的大小。