Android自定義控制元件系列一:如何測量控制元件尺寸
測量控制元件尺寸(寬度、高度)是開發自定義控制元件的第一步,只有確定尺寸後才能開始畫(利用canvas在畫布上畫,我們所使用的控制元件實際上都是這樣畫上去的)。當然,這個尺寸是需要根據控制元件的各個部分計算出來的,比如:padding、文字大小,間距等。
非容器控制元件的onMeasure
下面我們就來看看如何給非容器控制元件(即直接extends View)這隻尺寸的:
1.
@Override
2.
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec) {
3.
setMeasuredDimension(measureWidth(widthMeasureSpec),
4.
}
通過重寫onMeasure()方法來設定尺寸。這個方法實際是由容器控制元件(LinearLayout、RelativeLayout)來呼叫的,以便容器控制元件知道我需要分配給你多大空間或尺寸。
計算完尺寸後,最終呼叫setMeasuredDimension()來設定。比如,我要建立個 寬度是200px,高度是100px的控制元件,就可以setMeasuredDimension(200, 100)。
注意setMeasuredDimension()接收的值是px值,而不是dp值,所以我們不可能像上面那樣將尺寸寫固定的。如何做那:
引數 int widthMeasureSpec, int heightMeasureSpec 是指明控制元件可獲得的空間以及關於這個空間描述的元資料,是與佈局檔案中android:layout_width、android:layout_height相聯絡的。如何使用:
01.
/**
02.
* 計算元件寬度
03.
*/
04.
private
int
measureWidth(
int
widthMeasureSpec) {
05.
int
result;
06.
int
specMode = MeasureSpec.getMode(widthMeasureSpec);
07.
int
specSize = MeasureSpec.getSize(widthMeasureSpec);
08.
09.
if
(specMode == MeasureSpec.EXACTLY) {
//精確模式
10.
result = specSize;
11.
}
else
{
12.
result = getDefaultWidth();//最大尺寸模式,getDefaultWidth方法需要我們根據控制元件實際需要自己實現
13.
if
(specMode == MeasureSpec.AT_MOST) {
14.
result = Math.min(result, specSize);
15.
}
16.
}
17.
return
result;
18.
}
呼叫MeasureSpec.getMode(measureSpec),可以獲得設定尺寸的mode(模式)。
mode共有三種情況,取值分別為:
MeasureSpec.UNSPECIFIED,MeasureSpec.EXACTLY,MeasureSpec.AT_MOST。
MeasureSpec.EXACTLY是精確尺寸,當我們將控制元件的layout_width或layout_height指定為具體數值時如andorid:layout_width="50dip",或者為FILL_PARENT是,都是控制元件大小已經確定的情況,都是精確尺寸。
MeasureSpec.AT_MOST是最大尺寸,當控制元件的layout_width或layout_height指定為WRAP_CONTENT時,控制元件大小一般隨著控制元件的子空間或內容進行變化,此時控制元件尺寸只要不超過父控制元件允許的最大尺寸即可。因此,此時的mode是AT_MOST,size給出了父控制元件允許的最大尺寸。
MeasureSpec.UNSPECIFIED是未指定尺寸,這種情況不多,一般都是父控制元件是AdapterView,通過measure方法傳入的模式。
measureWidth() 方法是個固定實現,幾乎不用改, 唯一需要我們實現的就是getDefaultWidth()。即,當mode處於最大模式下,此時父容器會將他所能給你的或者說目前剩餘的最大空間給你。你只能在這個空間內設定控制元件尺寸。一個簡單的類似TextView的getDefaultWidth()的實現:
1.
private
void
getDefaultWidth(){
2.
int
txtWidth = (
int
)
this
.paint.measureText(
this
.text);
3.
return
txtWidth +
this
.paddingLeft +
this
.paddingRight;
4.
}
上面說了寬度的測量,高度同理:
01.
/**
02.
* 計算元件高度
03.
*/
04.
private
int
measureHeight(
int
measureSpec) {
05.
int
result;
06.
int
specMode = MeasureSpec.getMode(measureSpec);
07.
int
specSize = MeasureSpec.getSize(measureSpec);
08.
09.
if
(specMode == MeasureSpec.EXACTLY) {
10.
result = specSize;
11.
}
else
{
12.
result = getDefaultHeight();
13.
if
(specMode == MeasureSpec.AT_MOST) {
14.
result = Math.min(result, specSize);
15.
}
16.
}
17.
return
result;
18.
}
有了上面的實現,我們就可以在佈局檔案中使用控制元件時,通過android:layout_width、android:layout_height來自定義控制元件的寬度、高度。
容器控制元件的onMeasure
容器控制元件一般是繼承ViewGroup,ViewGroup是個抽象類,本身沒有實現onMeasure,但是他的子類都有各自的實現,通常他們都是通過measureChildWithMargins函式或者其他類似於measureChild的函式來遍歷測量子View,被GONE的子View將不參與測量,當所有的子View都測量完畢後,才根據父View傳遞過來的模式和大小來最終決定自身的大小.
在測量子View時,會先獲取子View的LayoutParams,從中取出寬高,如果是大於0,將會以精確的模式加上其值組合成MeasureSpec傳遞子View,如果是小於0,將會把自身的大小或者剩餘的大小傳遞給子View,其模式判定在前面表中有對應關係.
ViewGroup一般都在測量完所有子View後才會呼叫setMeasuredDimension()設定自身大小。
==========================================================================================
歡迎加入我們的技術交流群:
Android群: 66756039
JavaEE群: 361579846