Android自訂控制項系列一:如何測量控制項尺寸

來源:互聯網
上載者:User

Android自訂控制項系列一:如何測量控制項尺寸

 

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

 

非容器控制項的onMeasure

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

1. @Override 2. 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()設定自身大小。

 

 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.