Android 畫圖類View與SurfaceView之學習

在開發遊戲開發中,android相應的提供瞭幾個重要的模塊:

1、顯示界面的視圖:  Android 提供 View 和 SurfaceView 

2、控制遊戲整體結構: android 提供 Activity 

3、邏輯控制類:專門用於處理遊戲的邏輯計算

4、處理遊戲界面與用戶交互事件 : 利用 View 類提供的 onKeyDown onKeyUp onTounchEvent等方法

 

我們這裡簡單熟悉一下如何在視圖上畫東西。

 

1、View 類: android.view.View 

View 是Android中的一個超類,這個類幾乎包含瞭所有的屏幕類型。每一個View都有一個用於繪畫的畫佈,這個畫佈 可以進行任何擴展。

任何一個View類都隻需要重寫onDraw方法來實現界面顯示,任何一個View都隻需要重寫 OnDraw 方法來實現界面顯示,自定義的視圖可以是復雜的3D實現,也可以是非常簡單的文本或位圖。

 

Android 中有個重要的東東: Android UI 線程 

在這裡 https://blog.csdn.net/andyhuabing/article/details/11921887 有對其簡要精典的介紹及註意點,這裡就不再重復說明瞭。

 

這裡來個簡單例子說明一下View的用法,利用線程變更顯示顏色,通過上下左右移動矩形

 

TestView.java 類如下:

[java] 

package com.example.testondraw;  

  

import android.content.Context;  

import android.graphics.Canvas;  

import android.graphics.Color;  

import android.graphics.Paint;  

import android.view.View;  

  

/** 

 * View 是Android中的一個超類,這個類幾乎包含瞭所有的屏幕類型。每一個View都有一個用於繪畫的畫佈,這個畫佈 可以進行任何擴展。 

 *  

 * 任何一個View類都隻需要重寫onDraw方法來實現界面顯示。 

 *  

 */  

public class TestView extends View {  

    int miCount = 0;  

    int x = 10, y = 10;  

  

    public TestView(Context context) {  

        super(context);  

    }  

  

    @Override  

    protected void onDraw(Canvas canvas) {  

        if (miCount < 100)  

        {  

            miCount++;  

        }  

        else   

        {  

            miCount = 0;  

        }  

        //繪圖  

        Paint mPaint = new Paint();     

        switch (miCount%4)  

        {  

        case 0:  

            mPaint.setColor(Color.BLUE);      

            break;  

        case 1:  

            mPaint.setColor(Color.GREEN);     

            break;  

        case 2:  

            mPaint.setColor(Color.RED);  

            break;  

        case 3:  

            mPaint.setColor(Color.YELLOW);  

            break;  

        default:  

            mPaint.setColor(Color.WHITE);  

            break;  

        }  

        // 繪制矩形  

        canvas.drawRect(x, y, x + 120, y + 80, mPaint);  

    }  

}  

 

如何在 Acitivty 使用呢?示例代碼如下:

[java]  

package com.example.testondraw;  

  

import java.util.List;  

  

import android.app.Activity;  

import android.app.ActivityManager;  

import android.content.Context;  

import android.graphics.Bitmap;  

import android.os.Bundle;  

import android.os.Handler;  

import android.os.Message;  

import android.util.DisplayMetrics;  

import android.util.Log;  

import android.view.KeyEvent;  

import android.view.Menu;  

  

public class MainActivity extends Activity {  

    static final String TAG = "MainActivity";  

    private TestView mTestView = null;  

    private TestSurfaceView mTestSurfaceView = null;  

    private int mWidth = 0, mHeight = 0;  

    private MyHandler mHandler = new MyHandler();  

  

    @Override  

    public void onCreate(Bundle savedInstanceState) {  

        super.onCreate(savedInstanceState);  

  

        initView();  

  

        startTestView();  

          

        // setContentView(R.layout.main);  

    }  

  

    void initView() {  

        // 使用自定義的View  

        mTestView = new TestView(this);  

        setContentView(mTestView);  

      

        DisplayMetrics dm = new DisplayMetrics();  

        getWindowManager().getDefaultDisplay().getMetrics(dm);  

        mWidth = dm.widthPixels;  

        mHeight = dm.heightPixels;  

        Log.i(TAG, "Display Metrics width:" + mWidth + " mHeight:" + mHeight);  

    }  

  

    @Override  

    public boolean onCreateOptionsMenu(Menu menu) {  

        getMenuInflater().inflate(R.menu.main, menu);  

        return true;  

    }  

  

    void startTestView() {  

        Thread th = new Thread(new Runnable() {  

  

            @Override  

            public void run() {  

                while (!Thread.currentThread().isInterrupted()) {  

                    // 使用postInvalidate可以直接在線程中更新界面  

//                  mTestView.postInvalidate();  

                      

                    // 使用發消息給UI線程  

                    Message msg = Message.obtain();  

                    msg.what = 1;  

                    mHandler.sendMessage(msg);  

  

                    try {  

                        Thread.sleep(100);  

                    } catch (InterruptedException e) {  

                        Thread.currentThread().interrupt();  

                    }  

                }  

            }  

        });  

        th.start();  

    }  

  

    class MyHandler extends Handler {  

        @Override  

        public void handleMessage(Message msg) {  

            switch (msg.what) {  

            case 1:  

                mTestView.invalidate();  

                break;  

            default:  

                break;  

            }  

            super.handleMessage(msg);  

        }  

    }  

  

    public boolean onKeyDown(int keyCode, android.view.KeyEvent event) {  

        switch (keyCode) {  

        case KeyEvent.KEYCODE_DPAD_DOWN:  

            if (mTestView.y >= mHeight)  

                mTestView.y = 0;  

            mTestView.y += 10;  

            break;  

        case KeyEvent.KEYCODE_DPAD_UP:  

            if (mTestView.y <= 0)  

                mTestView.y = mHeight;  

            mTestView.y -= 10;  

            break;  

        case KeyEvent.KEYCODE_DPAD_LEFT:  

            if (mTestView.x <= 0)  

                mTestView.x = mWidth;  

            mTestView.x -= 10;  

            break;  

        case KeyEvent.KEYCODE_DPAD_RIGHT:  

            if (mTestView.x >= mWidth)  

                mTestView.x = 0;  

            mTestView.x += 10;  

            break;  

        }  

        return false;  

    };  

}  

 

2、SurfaceView  android.view.SurfaceView

當執行效率有要求很高時,View類就無法滿足需求。必須使用 SurfaceView 類 — 利用雙緩沖技術

 

使用SurfaceView提供給需要直接畫像素而不是使用畫窗體部件的應用使用。

而每個Surface創建一個Canvas對象,用來管理View在Surface上的繪畫操作。

 

簡要說明一下具體的方法及使用:

SurfaceHolder 對象需要通過 getHolder() 獲取

在 Layout 上擺一個 SurfaceView 組件:

mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView1);

SurfaceHolder holder = mSurfaceView.getHolder();

 

對於SurfaceView 的創建,銷毀及變更

@SuppressWarnings("unused")

private SurfaceHolder.Callback mSurfaceCbk = new SurfaceHolder.Callback() {

 

@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width,

int height) {    // 在Surface 大小發生變更時觸發

// TODO Auto-generated method stub

 

}

 

 

@Override

public void surfaceCreated(SurfaceHolder holder) { // 在創建 Surface 時觸發

// TODO Auto-generated method stub

 

}

 

@Override

public void surfaceDestroyed(SurfaceHolder holder) { // 在銷毀 Surface 時觸發

// TODO Auto-generated method stub

 

}

};

 

添加及刪除 SurfaceView 的回調函數:

display.addCallback(mSurfaceCbk);

display.removeCallback(mSurfaceCbk);

 

Canvas 的使用:

lockCanvas  鎖定畫佈,繪圖之前必須鎖定畫佈才能得到當前畫佈對象

unlockCanvasAndPost 開始繪制時鎖定畫佈,繪制完成之後解鎖畫佈

 

下面例子是繪制一個不斷變換顏色的圓,並實現 SurfaceView 的事件處理

TestSurfaceView.java 代碼:

[java] 

package com.example.testondraw;  

  

import android.content.Context;  

import android.graphics.Bitmap;  

import android.graphics.Canvas;  

import android.graphics.Color;  

import android.graphics.Paint;  

import android.view.SurfaceHolder;  

import android.view.SurfaceView;  

  

/** 

 * 使用SurfaceView提供給需要直接畫像素而不是使用畫窗體部件的應用使用。 

 * 而每個Surface創建一個Canvas對象,用來管理View在Surface上的繪畫操作。 

 */  

public class TestSurfaceView extends SurfaceView implements  

        SurfaceHolder.Callback {  

    // 控制循環  

    private boolean mbLoop = false;  

  

    // 定義SurfaceHolder對象  

    private SurfaceHolder mSurfaceHolder = null;  

    private int miCount = 0;  

    public int x = 50, y = 50;  

    private int mWidth = 1280,mHeight = 720;  

    private Bitmap mBitmap = null;  

      

    public TestSurfaceView(Context context) {  

        super(context);  

  

        // 實例化SurfaceHolder  

        mSurfaceHolder = this.getHolder();  

  

        // 添加回調  

        mSurfaceHolder.addCallback(this);  

        this.setFocusable(true);  

    }  

  

    public void setDisplayWH(int w, int h) {  

        mWidth = w;  

        mHeight = h;  

    }  

  

    public void setBitmap(Bitmap bitmap) {  

        this.mBitmap = bitmap;  

    }  

  

    @Override  

    public void surfaceChanged(SurfaceHolder holder, int format, int width,  

            int height) {  

        // TODO Auto-generated method stub  

          

    }  

  

    @Override  

    public void surfaceCreated(SurfaceHolder holder) {  

        mbLoop = true;  

  

        Thread th = new Thread(new Runnable() {  

  

            @Override  

            public void run() {  

                while (mbLoop){  

                    try {  

                        Thread.sleep(200);  

                    } catch (InterruptedException e) {  

                        e.printStackTrace();  

                    }  

  

                    synchronized( mSurfaceHolder ){  

                        drawBitmap();  

                        DrawData();  

                    }  

                }  

            }  

        });  

        th.start();  

    }  

  

    @Override  

    public void surfaceDestroyed(SurfaceHolder holder) {  

        mbLoop = false;  

    }  

  

    private void drawBitmap() {  

        // 鎖定畫佈,得到canvas  

        if (mSurfaceHolder == null || this.mBitmap == null)  

            return;  

  

        Canvas canvas = mSurfaceHolder.lockCanvas();  

        if (canvas == null) {  

            return;  

        }  

  

        // 繪圖  

        Paint paint = new Paint();  

        paint.setAntiAlias(true);  

        paint.setColor(Color.BLUE);  

  

        canvas.drawBitmap(this.mBitmap, 0, 0, paint);  

  

        // 繪制後解鎖,繪制後必須解鎖才能顯示  

        mSurfaceHolder.unlockCanvasAndPost(canvas);  

    }  

  

    // 繪圖方法  

    private void DrawData() {  

        if (mSurfaceHolder == null)  

            return;  

  

        // 鎖定畫佈,得到canvas  

        Canvas canvas = mSurfaceHolder.lockCanvas();  

        if (canvas == null) {  

            return;  

        }  

  

        if (miCount < 100) {  

            miCount++;  

        } else {  

            miCount = 0;  

        }  

  

        // 繪圖  

        Paint mPaint = new Paint();  

        mPaint.setAntiAlias(true);  

        mPaint.setColor(Color.BLACK);  

  

        // 繪制矩形–清屏作用  

        canvas.drawRect(0, 0, mWidth, mHeight, mPaint);  

          

        switch (miCount % 4) {  

        case 0:  

            mPaint.setColor(Color.BLUE);  

            break;  

        case 1:  

            mPaint.setColor(Color.GREEN);  

            break;  

        case 2:  

            mPaint.setColor(Color.RED);  

            break;  

        case 3:  

            mPaint.setColor(Color.YELLOW);  

            break;  

        default:  

            mPaint.setColor(Color.WHITE);  

            break;  

        }  

  

        // 繪制矩形–  

        canvas.drawCircle(x, y, 50, mPaint);  

          

        // 繪制後解鎖,繪制後必須解鎖才能顯示  

        mSurfaceHolder.unlockCanvasAndPost(canvas);  

    }  

}  

 

測試使用例子:

 

[java]  

package com.example.testondraw;  

  

import java.util.List;  

  

import android.app.Activity;  

import android.app.ActivityManager;  

import android.content.Context;  

import android.graphics.Bitmap;  

import android.os.Bundle;  

import android.os.Handler;  

import android.os.Message;  

import android.util.DisplayMetrics;  

import android.util.Log;  

import android.view.KeyEvent;  

import android.view.Menu;  

import android.view.SurfaceHolder;  

import android.view.SurfaceView;  

  

public class MainActivity extends Activity {  

    static final String TAG = "MainActivity";  

    private TestSurfaceView mTestSurfaceView = null;  

    private int mWidth = 0, mHeight = 0;  

  

    @Override  

    public void onCreate(Bundle savedInstanceState) {  

        super.onCreate(savedInstanceState);  

  

        initView();  

  

        startTestSurfaceView();  

          

        // setContentView(R.layout.main);  

    }  

  

    void initView() {  

        // 使用自定義的View  

        mTestSurfaceView = new TestSurfaceView(this);  

        setContentView(mTestSurfaceView);         

  

        DisplayMetrics dm = new DisplayMetrics();  

        getWindowManager().getDefaultDisplay().getMetrics(dm);  

        mWidth = dm.widthPixels;  

        mHeight = dm.heightPixels;  

        Log.i(TAG, "Display Metrics width:" + mWidth + " mHeight:" + mHeight);  

    }  

  

    @Override  

    public boolean onCreateOptionsMenu(Menu menu) {  

        getMenuInflater().inflate(R.menu.main, menu);  

        return true;  

    }  

  

    public boolean onKeyDown(int keyCode, android.view.KeyEvent event) {  

        switch (keyCode) {  

        case KeyEvent.KEYCODE_DPAD_DOWN:  

            if (mTestSurfaceView.y >= mHeight)  

                mTestSurfaceView.y = 0;  

            mTestSurfaceView.y += 10;  

            break;  

        case KeyEvent.KEYCODE_DPAD_UP:  

            if (mTestSurfaceView.y <= 0)  

                mTestSurfaceView.y = mHeight;  

            mTestSurfaceView.y -= 10;  

            break;  

        case KeyEvent.KEYCODE_DPAD_LEFT:  

            if (mTestSurfaceView.x <= 0)  

                mTestSurfaceView.x = mWidth;  

            mTestSurfaceView.x -= 10;  

            break;  

        case KeyEvent.KEYCODE_DPAD_RIGHT:  

            if (mTestSurfaceView.x >= mWidth)  

                mTestSurfaceView.x = 0;  

            mTestSurfaceView.x += 10;  

            break;  

        case KeyEvent.KEYCODE_BACK:  

            this.finish();  

            break;  

        }  

        return false;  

    };  

  

    void startTestSurfaceView() {  

        mTestSurfaceView.setDisplayWH(mWidth, mHeight);  

        }  

}  

 

發佈留言