在 View 的 onMeasure 回調(diào)中,有兩個參數(shù)widthMeasureSpec和heightMeasureSpec,分別代表當(dāng)前View的寬與高的MeasureSpec。
1. MeasureSpec 的本質(zhì)
MeasureSpec 的本質(zhì)是一個整型值。
眾所周知,Java/Kt 中的整型占用了 32 位;而 MeasureSpec 的前2位表示測量模式(mode),后30位表示測量大?。╯ize),實現(xiàn)了一個整型保存兩個信息的目的。
通過MeasureSpec.getMode(measureSpec)靜態(tài)方法可以獲取一個 MeasureSpec 的 mode,MeasureSpec.getSize(measureSpec)可以獲取到 size。
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val wMode = MeasureSpec.getMode(widthMeasureSpec) // 水平方向的測量模式
val wSize = MeasureSpec.getSize(widthMeasureSpec) // 水平方向的測量大小
val hMode = MeasureSpec.getMode(heightMeasureSpec) // 豎直方向的測量模式
val hSize = MeasureSpec.getSize(heightMeasureSpec) // 豎直方向的測量大小
}
通過MeasureSpec.makeMeasureSpec(size, mode)靜態(tài)方法,可以將 mode 與 size 合成為一個 MeasureSpec。
size 不用多說,表示寬或高的長度,單位是像素。
mode 分為三種:MeasureSpec.UNSPECIFIED、MeasureSpec.EXACTLY、MeasureSpec.AT_MOST;其中,MeasureSpec.UNSPECIFIED基本不用,可以忽視。也就是說,常用的 MeasureSpec 只有EXACTLY與AT_MOST兩種。
顧名思義,EXACTLY即為精確模式,表示這個 ViewGroup 的寬或高是確定值size;而AT_MOST則是最大模式,表示這個 View 的寬或高最大能達(dá)到size,真實長度根據(jù)實際情況而定。
在onMeasure回調(diào)中,傳入的 MeasureSpec 是根據(jù)當(dāng)前 View 的父容器的 MeasureSpec 和當(dāng)前 View 在布局中的layout_width、layout_height 屬性決定的。
2. MeasureSpec 的計算過程
那么,onMeasure中的兩個參數(shù)是如何得到的呢?
事實上,一般來講,當(dāng)一個 ViewGroup 測量自身的時候,會先遍歷它的子 View,并調(diào)用子 View 的measure方法進(jìn)行測量,而measure方法又會調(diào)用onMeasure回調(diào)。
也就是說,View的onMeasure回調(diào)方法中的兩個參數(shù)是父容器計算并傳遞過來的。
下表反應(yīng)了正常情況下父容器是如何得到一個子 View 的 MeasureSpec 的。其中橫軸表示父容器的 MeasureSpec,縱軸表示了該 View 在布局文件中設(shè)置的寬或高,表中為該 View 的 MeasureSpec 的 mode 與 size。
| 子View在xml中 \ 父MeasureSpec | EXACTLY | AT_MOST |
|---|---|---|
| 確定的數(shù)值 | mode: EXACTLY size: 布局中設(shè)置的大小 |
mode: EXACTLY size: 布局中設(shè)置的大小 |
| wrap_content | mode: AT_MOST size: 父容器specSize |
mode: AT_MOST size: 父容器specSize |
| match_parent | mode: EXACTLY size: 父容器specSize |
mode: AT_MOST size: 父容器specSize |
也就是說:
- 當(dāng)布局中寬或高填寫的固定值,那么對應(yīng)的 mode 為 EXACTLY ,size 為填寫的值;
- 當(dāng)布局中的寬或高填寫的
wrap_content,那么對應(yīng)的 mode 為 AT_MOST,size 為父容器的 size; - 當(dāng)布局中的寬或高填寫的
match_parent,那么顧名思義,mode 和 size 都與父容器相同。