1. 程式人生 > >Android群英傳---View、ViewGroup的測量和繪製

Android群英傳---View、ViewGroup的測量和繪製

View的測量

系統通過onMeasure()對View進行測量

1.系統提供MeasureSpec類幫助測量View

  • MeasureSpec是32bit的int值,期中高2bit為測量的模式,低30位為測量的大小。用於提高效率
測量的模式如下:
1. ++EXACTLY++

精確值模式:
控制元件屬性width or height設定為具體數值,如100dp,或者指定為match_parent屬性,就是該模式。

2. ++AT_MOST++

最大值模式:
控制元件屬性width or height設定為wrap_content。此時控制元件的尺寸不超過父控制元件允許的最大尺寸即可。

3. ++UNSPECIFIED++

不指定大小的測量模式,View想多大就多大,通常情況下載繪製自定義View時才會使用。

  • View類預設的onMeasure()方法只支援EXACTLY,。
  • 自定義控制元件想使用wrap_content就必須重寫onMeasure()來指定wrap_content時的大小。
  • *

2. View測量例項

onMeasure()如下
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

可以發現,本質上系統最終會呼叫setMeasuredDimension(int measuredWidth, int measuredHeight),將測量之後的寬高值設定進去。所以重寫onMeasure()最終是將測量後的寬高值設定給該方法。

因此重寫onMeasure()方法如下

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
}
  • 其中measureWidthormeasureHeight用來測量高度和寬度,程式碼如下:
//測量寬度
    private int measureWidth(int widthMeasureSpec){
        //提取出模式以及尺寸
        int specMode = MeasureSpec.getMode(widthMeasureSpec);
        int specSize = MeasureSpec.getSize(widthMeasureSpec);

        int alone  = 0;

        //精確值模式: 例如200dp or match_parent
        if(specMode == MeasureSpec.EXACTLY){
            alone = specSize;
        }else{
            alone = 200; //?????
            //最大值模式: wrap_parent
            if(specMode == MeasureSpec.AT_MOST){
                alone = Math.min(alone, specSize);
            }
        }
        return alone;
    }
    //測量高度
    private int measureHeight(int heightMeasureSpec){
        //提取出模式以及尺寸
        int specMode = MeasureSpec.getMode(heightMeasureSpec);
        int specSize = MeasureSpec.getSize(heightMeasureSpec);

        int result  = 0;

        //精確值模式: 例如200dp or match_parent
        if(specMode == MeasureSpec.EXACTLY){
            result = specSize;
        }else{
            result = 200; //?????
            //最大值模式: wrap_parent
            if(specMode == MeasureSpec.AT_MOST){
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

其中result = 200;表示在wrap_content屬性的時候獲得預設的200px作為長度

View的繪製

通常通過繼承View並且重寫它的onDraw()方法來完成繪圖。

onDraw()中有一個引數,就是Canvas canvas物件。使用這個Canvas物件就可以進行繪圖了,其他物件需要用程式碼建立。

Canvas canvas = new Canvas(bitmap);

為什麼要傳入bitmap?

不傳入bitmap也不會報錯,但是不要這麼做。bitmap是用來儲存所有繪製在Canvas上的畫素資訊。之後所有的Canvas.drawXXX都在該bitmap上。

Bitmap bitmap1 = Bitmap.createBitmap(new int[]{R.color.colorAccent, R.color.colorPrimary}, 200, 100, Bitmap.Config.RGB_565);

canvas.drawBitmap(bitmap1, 0,0,null);

ViewGroup的測量

ViewGroup用於管理子View,在控制顯示大小時,如果ViewGroup設定為wrap_content時,就需要遍歷所有子View來確定大小

遍歷子View就是呼叫其onMeasure方法進行測量。View擺放位置就是呼叫ViewGroup的layout方法,其內部遍歷子View的layout方法來指定具體位置。

自定義ViewGroup

通常重寫onLayout()控制子View的顯示位置。
如果支援wrap_content,必須重寫onMeasure()

ViewGroup一般不需要繪製

設定ViewGroup背景顏色的時候,呼叫onDraw()方法

ViewGroup會呼叫dispatchDraw()方法來繪製子View,其通過遍歷子View,並且呼叫其繪製方法。