對View的onMeasure()方法的進一步研究,viewonmeasure
在Android開發中,很多人對自訂View是望而生畏,但這又是向進階進階的必經之路,主要是對View裡面的很多方法不知道怎麼理解,其中一個就是onMeasure()方法。
首先,我自訂一個MyView,繼承於View,onMeasure()方法不做處理,直接調用super.onMeasure(widthMeasureSpec, heightMeasureSpec);
public class MyView extends View{ public MyView(Context context) { super(context); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); } public MyView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public MyView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); }}
布局檔案為:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <com.example.customviewdemo.View.MyView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="10dp" android:padding="10dp" android:background="#ff0000"/></LinearLayout>
onMeasure()方法的作用就是測量View需要多大的空間,就是寬和高,在MyView中我沒有做任何處理,使用View預設的測量規則,我們看下效果:
在android:layout_width和android:layout_height都為match_parent的時候,MyView填滿全屏,當我們把android:layout_width和android:layout_height都為wrap_content的時候,我們看到MyView還是填滿全屏,當我把android:layout_width和android:layout_height都這是為100dp的時候,我們看下效果
我們看到MyView的大小為100dp了。
結論:
1、View預設的測量規則是android:layout_width和android:layout_height為match_parent或者wrap_content時,是填充全屏的。
2、android:layout_width和android:layout_height設定為具體值時,那麼是多少,寬高就是多少。
顯然,預設的規則大部分不符合我們的需求,先來看下onMeasure()的參數,有兩個參數,widthMeasureSpec,heightMeasureSpec,以前不明白,我以為是View本身的大小,仔細想想也不對,如果是本身的大小那還要你測什麼啊,這兩個參數是父布局給它提供的水平和垂直的空間要求,大家注意,只是父布局提供的要求,當然View也可以不遵守在View的android:layout_width和android:layout_height的值就是onMeasure()兩個參數。什麼意思,比如我為android:layout_width和android:layout_height設定的值為300dp,但是我在onMeasure()中,測量時不遵守這個300dp的空間要求,將onMeasure()的實現改為:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(100,100); }
這樣一樣,不管android:layout_width和android:layout_height設定的值為多少,MyView顯示的寬高都為100px,一般來說我們不這樣做,我們要考慮父布局給出的寬高,即我們設定android:layout_width和android:layout_height的值。
結論:
onMeasure方法的作用就是計算出自訂View的寬度和高度。這個計算的過程參照父布局給出的大小,以及自己特點算出結果
一般來說使用如下的實現過程:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); } private int measureWidth(int measureSpec) { int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); //設定一個預設值,就是這個View的預設寬度為500,這個看我們自訂View的要求 int result = 500; if (specMode == MeasureSpec.AT_MOST) {//相當於我們設定為wrap_content result = specSize; } else if (specMode == MeasureSpec.EXACTLY) {//相當於我們設定為match_parent或者為一個具體的值 result = specSize; } return result; } private int measureHeight(int measureSpec) { int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); int result = 500; if (specMode == MeasureSpec.AT_MOST) { result = specSize; } else if (specMode == MeasureSpec.EXACTLY) { result = specSize; } return result; }