android View的測量和繪製

來源:互聯網
上載者:User

標籤:

本篇內容來源於android 群英傳(徐易生著)

我寫到這裡,是覺得徐易生講的確實很好, 另外加入了一些自己的理解,便於自己基礎的提高.

如果要繪製一個View , 就需要先取測量它,也就是需要知道它的大小和位置. 這樣我們就能在螢幕中滑出來它了.這個過程是在onMeasure()方法中完成的.

一.測量模式

測量view的大小時,需要用到MeasureSpec (測量規範)這個類來指定測量模式 ,一共有3種

EXACTLY (精確模式) , 系統預設值.
如果我們指定控制項寬高為 xxdp, xxpx,match_parent(填充父view大小) 這3個中的任意一個那它就是精確模式.

AT_MOST (最大值模式)
這個最大值是啥意思呢? 迷茫很久, 比如父控制項的子控制項(1個或多個),而子控制項的大小又是warp_content ,那麼控制項的大小就會隨著它子控制項的內容變化而變化, 此時子控制項的尺寸只有不超過父控制項允許的最大尺寸即可.
比如父控制項指定大小為200 ,那麼我們可以這麼取值

result = 200;if (specMode == MeasureSpec.AT_MOST) {      result = Math.min(result, specSize);//取出 2者最小值,如果specSize(子控制項大小)大於父控制項規定的200,就使用200,否則使用specSize}

UNSPECIFIED

這個屬性用的不是太多. --它不指定其大小測量模式,View想多大就多大,通常情況下繪製自訂View才會使用.

 

二.什麼時候使用onMeasure()

首先要說明的一點是, 這個方法不是必須重寫的. View類預設的onMeasure()只支援EXACTLY(精確)模式,如果在自訂控制項是不重寫它,就只能使用EXACTLY模式. 控制項可以響應你指定的具體寬高dp或px或match_parent屬性.

而如果要想讓自訂View支援wrap_content屬性,那麼就必須重寫onMeasure方法來指定warp_content時的大小.

通過MeasureSpec這個類,我們就擷取了View的"測量模式"和View想要繪製的大小. 有了這些資訊,我們就可以控制View最終顯示的大小.

首先來看一下onMeasure()的方法

/**     * 測量View的大小     * 首先這個方法不是必須要重寫的.(只有控制項使用wrap_content時,才必須重寫該方法)     * 在自訂view時, MeasureSpec 這個測量規範類,定義了3中測量規範: exactly, at_most,unspecified     * 如果我們不重寫onMeasure() ,系統預設測量規範是並且只能是exactly      * 重寫了該方法,就可以使用以上3種模式的任意一個     */    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        //父類onMeasure內部調用了setMeasuredDimension(寬,高) 將最終控制項測量的寬高值填進去        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }

 

三.View的繪製

當測量好一個View,就可以繪製他了.繪製需要在 onDraw(Canvas canvas)方法中繪製, 需要用到 Canvas 畫布類和Paint 畫筆類.

這個方法攜帶了一個Canvas參數,我們可以建立一個Paint對象就可以在這個畫布上面繪製了. 當然如果其他地方需要用到畫布, 我們一般會單獨建立一個 Canvas canvas=new Canvas(bitmap);

/**     * 建立canvas 畫布時,一般都用這個構造方法.而不用無參構造方法.     * 因為這個Bitmap是用來儲存Canvas畫布上面的像素資訊的.     * 這個過程我們稱之為裝載畫布,所以這種方式建立畫布後,後面調用的所有canvas.drawXxx();方法都發生在這個bitmap上.     */    Canvas canvas = new Canvas(bitmap);

四.ViewGroup的測量

ViewGroup ,比如LinearLayout它要去管理它的子View, 其中一個管理項目就是負責 內部子View的顯示大小. 當LinearLayout大小為warp_content時,LinearLayout就需要對子View進行遍曆, 一遍擷取所有子View的大小,從而決定自己的大小. 而在其他模式下則會通過具體制定值來設定自身大小.

LinearLayout在測量時通過遍曆所有子View,從而調用子View的Measure方法來擷取每一個子View測量結果. 當測量完畢後,就要擺放這些子View的位置了, 需要重寫onLayout , 遍曆調用子view的onlayout 來確定子view位置.

注意: 在自訂ViewGroup時,通常會重寫 onLayout來控制其子view顯示的位置, 同時如果需要wrap_content屬性,還需要重寫onMeasure() 來測量子view的大小.

五.ViewGroup的繪製

通常情況下ViewGroup不需要繪製,因為它就是一個容器,本身沒什麼好繪製的,只有它指定了背景色,才會調用它的onDraw,否則不會調用.但是ViewGroup會使用dispatchDraw() 方法來繪製其子View, 過程和通過遍曆所有子View,並調用子View的onDraw()方法來完成繪製是一樣的.

六.自訂View

自訂View需要注意的幾個回調方法

onFinishInflate()  xml 載入後回調

onSizeChanged()  大小改變後回調

onMeasure()  測量控制項大小

onLayout()  設定控制項位置

onTouchEvent()  控制項觸摸事件

自訂View並不需要重寫以上所有回調,根據需要進行即可.

六. 什麼樣情況下才使用自訂View?

對現有控制項擴充

通過組合來實現新控制項

重寫View來實現全新控制項

6.1 對現有控制項擴充

比如一個TextView,我們要給他繪製幾層背景, 比如給他繪製2層背景,然後最上面是文字. 原生的TextView使用onDraw方法用於繪製要顯示的文字,也就是 調用 super.onDraw(); 

所以我們在extends TextView之後,需要重寫 onDraw, 然後我們可以在這裡 繪製2個 矩形背景,最後要調用super.onDraw用於顯示文字.

public class MyTextView extends TextView {    private Paint mPaint1, mPaint2;    public MyTextView(Context context) {        super(context);        initView();    }    public MyTextView(Context context, AttributeSet attrs) {        super(context, attrs);        initView();    }    public MyTextView(Context context, AttributeSet attrs,                      int defStyleAttr) {        super(context, attrs, defStyleAttr);        initView();    }    private void initView() {        mPaint1 = new Paint();        mPaint1.setColor(getResources().getColor(                android.R.color.holo_blue_light));        mPaint1.setStyle(Paint.Style.FILL);        mPaint2 = new Paint();        mPaint2.setColor(Color.YELLOW);        mPaint2.setStyle(Paint.Style.FILL);    }    @Override    protected void onDraw(Canvas canvas) {        // 繪製外層矩形        canvas.drawRect(                0,                0,                getMeasuredWidth(),                getMeasuredHeight(),                mPaint1);        // 繪製內層矩形        canvas.drawRect(                25,                25,                getMeasuredWidth() - 25,                getMeasuredHeight() - 25,                mPaint2);        canvas.save(); //在畫布移動,旋轉,縮放之前,需要先儲存它的狀態        // 繪製文字前平移10像素        canvas.translate(10, 0);        // 調用父類完成的方法,即繪製文本        super.onDraw(canvas);        canvas.restore();//取出儲存後的狀態,他和Canvas.save是對應的.    }}

6.2建立複合控制項

比如建立一個自訂的通用TopBar

...後續明天再寫

 

android View的測量和繪製

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.