1. 程式人生 > >Android自定義控制元件系列一:如何測量控制元件尺寸

Android自定義控制元件系列一:如何測量控制元件尺寸

測量控制元件尺寸(寬度、高度)是開發自定義控制元件的第一步,只有確定尺寸後才能開始畫(利用canvas在畫布上畫,我們所使用的控制元件實際上都是這樣畫上去的)。當然,這個尺寸是需要根據控制元件的各個部分計算出來的,比如:padding、文字大小,間距等。

非容器控制元件的onMeasure

下面我們就來看看如何給非容器控制元件(即直接extends View)這隻尺寸的:

1.@Override2.protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {3.setMeasuredDimension(measureWidth(widthMeasureSpec), 
measureHeight(heightMeasureSpec));
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