天天看點

Android學習筆記之-自定義View執行個體及View的繪制過程(一)

View元件的作用類似于 Swing 程式設計中的JPanel, 它隻是一個矩形的空白區域, View元件沒有任何内容. 對于Android 應用的其他UI元件來說, 他們都繼承了View元件. 然後在View元件提供的空白區域上繪制外觀.

基于Android UI 元件的實作原理, 開發者完全開發出項目定制的元件—-當Android系統提供的UI元件不足以滿足項目需求時, 開發者可以通過繼承View 來派生自定義元件.

基于Android UI 元件的實作原理, 開發者完全開發出項目定制的元件—-當Android系統提供的UI元件不足以滿足項目需求時, 開發者可以通過繼承View 來派生自定義元件.

開發者打算派生自己UI元件時, 首先定義一個繼承View基類的子類, 然後重寫View類的一個或多個方法, 通常可以被使用者重寫的方法如下.

構造器: 重寫構造器是定制View的最基本方式, 當Java代碼建立一個View執行個體, 或根據XML布局檔案加載并建構界面時将需要調用構造器.

> onFinishInflate(): 這是一個回調方法, 當應用從XML布局檔案加載該元件并利用它來建構界面之後, 該方法将會回調.

> onMeasure(int, int):調用該方法來檢測View元件及它所包含的所有子類元件的大小.

> onLayout(boolean, int, int, int, int):當該元件需要配置設定其子元件的位置-大小時, 該方法就會被調用.

> onSizeChanged(int, int, int, int):當該元件的大小被改變時回調該方法.

> onDraw(Canvas):當該元件将要繪制它的内容時回調該方法進行繪制.

> onKeyDown(int, KeyEvent):當某個鍵被按下時觸發該方法.

> onKeyUp(int, KeyEvent):當松開某個鍵時觸發該方法.

> onTrackballEvent(MotionEvent):當發生軌迹球事件時觸發該方法.

> onTouchEvent(MotionEvent):當發生觸摸屏事件時觸發該方法.

> onWindowFocusChanged(boolean):當該元件得到-失去焦點時觸發該方法.

> onAttachedToWindow():當把該元件放入某個視窗時觸發該方法.

> onDetachedFromWindow():當把該元件從某個視窗上分離時觸發該方法.

> onWindowVisibilityChanged(int):當包含該元件的視窗的可見性發生改變時觸發該方法.

當需要開發自定義View時, 開發者并不需要重寫上面列出的所有方法, 而是可以根據業務需求重寫上面的部分方法.

執行個體 : 跟随手指的小球.
package com.yuyang.customview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class DrawView extends View {

    private float currentX = ;
    private float currentY = ;

    /** 建立畫筆 */
    private Paint mPaint = new Paint();

    public DrawView(Context context) {
        this(context, null);
        // TODO Auto-generated constructor stub
    }

    public DrawView(Context context, AttributeSet attrs) {
        this(context, attrs, );
        // TODO Auto-generated constructor stub
    }

    public DrawView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // TODO Auto-generated constructor stub
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
        //設定畫筆顔色
        mPaint.setColor(Color.RED);
        //繪制一個小圓(作為小球)
        canvas.drawCircle(currentX, currentY, , mPaint);
    }

    //為該元件的觸碰事件重寫事件處理方法
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        //修改currentX currentY 兩個屬性
        currentX = event.getX();
        currentY = event.getY();
        //通知目前元件重繪自己
        invalidate();
        //傳回 true 表明該處理方法已經處理該事件
        return true;
    }
}
           

上面的DrawView元件繼承了View基類, 并重寫了onDraw方法—-該方法負責在該元件的指定位置繪制一個小球. 除此之外, 該元件還重寫了onTouchEvent(MotionEvent event)方法, 該方法用于處理該元件的觸碰事件, 當使用者手指觸碰該組事件時将會激發該方法.

有了這個自定義元件之後, 把這個自定義元件添加到XML檔案中就可以實作跟随手指移動的小球了.

View 繪制的流程

上面的例子比較簡單, 主要是為了說明自定義控件的屬性. 下面我們來說說View的繪制步驟.

View 系統獲得消息後, 會按照預設的邏輯來派發消息, 主要就是把該消息派發給所有的子視圖(View), 以便相應的子視圖能夠獲得消息并執行不同的任務. 如果任務是一個純背景任務, 即該任務不會引起任何界面的變化, 那麼View系統接下來僅僅按照預設邏輯繼續派發下一個使用者消息. 而如果該任務會引起界面變化, 那麼View系統則要重新繪制界面.

重繪界面的過程大緻分為三步:

  1. 計算該視窗中所有視圖的大小, 該步驟也稱之為測量(measure)過程, 即測量出所有視圖的實際尺寸大小. 但是, 并不是所有導緻界面重繪的操作都需要重新計算視窗的大小, 在View的内部邏輯中, 使用了一個内部變量儲存相應的狀态, 當使用者的某個操作導緻改變了視圖大小時, 會設定該變量, 而View的内部邏輯會根據該變量, 決定是否需要重新測量.
  2. 為所有視圖配置設定位置, 該步驟也稱之為布局(layout)過程, 即應該把視圖放在螢幕的什麼位置上.
  3. 把視圖繪制到螢幕上. 當系統獲知了該視窗中所有視圖的大小位置後, 就可以完全确定螢幕上應該顯示哪些視圖了. 在繪制時, 系統會為每一個視窗建立一個畫布(Canvas)對象, 并把這個Canvas對象傳遞給從根視圖到所有子視圖. View系統将Canvas傳遞給子視圖時, 都先将對該畫布進行一次裁切(Clip), 進而在子視圖看來, 總是從畫布的(0, 0)坐标開始繪制.

以上是本人通過各種手段擷取的View繪制機制, 對于純使用的同學應該是沒多大問題, 有想要深究同學還是推薦去看Android官方API.