在自定義view的時候,只需要知道以下三個步驟:
1.測量——onMeasure():決定View的大小
2.佈局——onLayout():決定View在ViewGroup中的位置
3.繪製——onDraw():如何繪製這個View。(非必須)
今天來總理下onMeasure()方法的理解:
首先onMeasure()方法到目前爲止我最熟悉的是用途是用來讓可以滑動的View不能滑動.
不知道你經常那他來做什麼,現在就來看看一個很簡單的不可滑動的ListView的寫法,以此來淺談一下onMeasure()的寫法 :
public class NoScrollListview extends ListView{
public NoScrollListview(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* 測量寬高來設置不可滑動
* @param widthMeasureSpec 寬的詳細測量值
* @param heightMeasureSpec 高的詳細測量值
*/
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
可以看到onMeasure()方法接收兩個參數寬高的詳細測量值 ,至於什麼叫詳細測量值 , 這裏不多解釋 , 反正就是預計的寬高 ,可以理解爲視圖View想要的大小(寬高)說明(想要的未必就是最終大小)。
然後在方法內部調用了MeasureSpec類的makeMeasureSpec()方法 , 還好這個類不是很大 ,還好搜到一篇詳細註釋的這個類 (附錄在最後), 現在一起來學習一下 這個方法:
/**
* 根據提供的size和mode得到一個詳細的測量結果
*/
// measureSpec = size + mode; (注意:二進制的加法,不是十進制的加法!)
// 這裏設計的目的就是使用一個32位的二進制數,32和31位代表了mode的值,後30位代表size的值
// 例如size=100(4),mode=AT_MOST,則measureSpec=100+10000...00=10000..00100
public static int makeMeasureSpec(int size, int mode) {
return size + mode;
}
這裏詳細介紹了makeMeausreSpec()方法 , 可以很清楚的明白我們改寫ListView測量的最大高度爲屏幕的高度,所以ListView就不能滑動了.
附錄MeasureSpec類詳解
/**
* MeasureSpec封裝了父佈局傳遞給子佈局的佈局要求,每個MeasureSpec代表了一組寬度和高度的要求
* MeasureSpec由size和mode組成。
* 三種Mode:
* 1.UNSPECIFIED
* 父不沒有對子施加任何約束,子可以是任意大小(也就是未指定)
* (UNSPECIFIED在源碼中的處理和EXACTLY一樣。當View的寬高值設置爲0的時候或者沒有設置寬高時,模式爲UNSPECIFIED
* 2.EXACTLY
* 父決定子的確切大小,子被限定在給定的邊界裏,忽略本身想要的大小。
* (當設置width或height爲match_parent時,模式爲EXACTLY,因爲子view會佔據剩餘容器的空間,所以它大小是確定的)
* 3.AT_MOST
* 子最大可以達到的指定大小
* (當設置爲wrap_content時,模式爲AT_MOST, 表示子view的大小最多是多少,這樣子view會根據這個上限來設置自己的尺寸)
*
* MeasureSpecs使用了二進制去減少對象的分配。
*/
public class MeasureSpec {
// 進位大小爲2的30次方(int的大小爲32位,所以進位30位就是要使用int的最高位和倒數第二位也就是32和31位做標誌位)
private static final int MODE_SHIFT = 30;
// 運算遮罩,0x3爲16進制,10進製爲3,二進制爲11。3向左進位30,就是11 00000000000(11後跟30個0)
// (遮罩的作用是用1標註需要的值,0標註不要的值。因爲1與任何數做與運算都得任何數,0與任何數做與運算都得0)
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
// 0向左進位30,就是00 00000000000(00後跟30個0)
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
// 1向左進位30,就是01 00000000000(01後跟30個0)
public static final int EXACTLY = 1 << MODE_SHIFT;
// 2向左進位30,就是10 00000000000(10後跟30個0)
public static final int AT_MOST = 2 << MODE_SHIFT;
/**
* 根據提供的size和mode得到一個詳細的測量結果
*/
// measureSpec = size + mode; (注意:二進制的加法,不是十進制的加法!)
// 這裏設計的目的就是使用一個32位的二進制數,32和31位代表了mode的值,後30位代表size的值
// 例如size=100(4),mode=AT_MOST,則measureSpec=100+10000...00=10000..00100
public static int makeMeasureSpec(int size, int mode) {
return size + mode;
}
/**
* 通過詳細測量結果獲得mode
*/
// mode = measureSpec & MODE_MASK;
// MODE_MASK = 11 00000000000(11後跟30個0),原理是用MODE_MASK後30位的0替換掉measureSpec後30位中的1,再保留32和31位的mode值。
// 例如10 00..00100 & 11 00..00(11後跟30個0) = 10 00..00(AT_MOST),這樣就得到了mode的值
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
}
/**
* 通過詳細測量結果獲得size
*/
// size = measureSpec & ~MODE_MASK;
// 原理同上,不過這次是將MODE_MASK取反,也就是變成了00 111111(00後跟30個1),將32,31替換成0也就是去掉mode,保留後30位的size
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
/**
* 重寫的toString方法,打印mode和size的信息,這裏省略
*/
public static String toString(int measureSpec) {
return null;
}
}