Android -SurfaceView-
SurfaceViewとは
SurfaceViewは、viewクラスを継承したクラスです。
Viewクラスよりも高速に描画ができ、ゲームプログラムに適しています。
SurfaceViewは、UIスレッドから独立して処理を行うビューです。
リアルタイムで処理を行うためには、UIスレッドから、独立したスレッドを起動を行う必要があります。
SurfaceViewは、アプリケーションのスレッドと行が処理のスレッドが独立している為、定期的な処理に向いています。
SurfaceViewの利用方法
- SurfaceViewクラスの継承とSerfaceHolder.Callbackインタフェースの実装をします。
- SerfaceHolderの生成
SerfaceHolderは、表面上を保っているオブジェクトへの抽象的なインタフェースで、 画面変化をモニタ(画面変化情報を取得)することを許可します。- getHolderメソッドで、SurfaceHolderオブジェクトを取得します。
- Surfaceイベントの通知先の指定を行います。
addCallback()メソッドを使用して、SurfaceViewの状態が変化した場合に呼び出されるSurfaceHolder.Callbackインタフェース を実装したクラスを指定
getHolder().addCallback(this);
通常は、SurfaceViewクラス自身にSurfaceHolder.Callbackインタフェースを実装することが多い為「this」と指定する ことが多いです。
- SurfaceViewCallbackインタフェースを実装するために3つのコールバックメソッドをオーバーライドします。
- SerfaceChanged()
SurfaceViewが変化した時のイベント処理
画面サイズの取得等 - SerfaceCreated()
SurfaceViewが作成された時のイベント処理 - SurfaceDestroyed()
SurfaceViewが破棄された時のイベント処理
リソースの解放
- SerfaceChanged()
- SurfaceViewにグラフィック描画
- lockCanvas()メソッドを使用して、ダブルバッファリングのロック処理をします
(他のスレッドからCanvasオブジェクトを操作させないようにする為です。) - Canvasオブジェクトを取得し、描画処理を行います。
- lockCanvas()メソッドを使用して、ダブルバッファリングのロック処理をします
- unlockCanvasAndPost()メソッドを使用して、ダブルバッファリングのアンロック処理を行います。
unlockCanvasAndPost()を呼んだ時に、Canvasオブジェクトに描画した内容が実画面に反映されます。
ダブルバッファリングとは
描画命令を実行ごとに画面に反映されるのではなく、複数の描画命令をまとめて画面に反映させることで、画面の チラつきを防ぐ処理のことです。
ViewクラスとSurfaceViewクラスの違い
- 画面の更新時にViewクラスでは、onDrawメソッドが自動的に呼ばれ、グラフィックの描画が行われますが、 SurfaceViewでは、呼び出されません。
- 再描画時Viewクラスでは、invalidateメソッドを実行するとonDrawメソッドが呼び出されていましたが、 SurfaceViewでは、invalidateメソッドを実行しても呼び出されることはありません。
- Viewクラスでは、定期的に画面を更新するタイマーとしてsendMessageDelayedメソッドを利用してましたが、 SurfaceViewは、別スレッドから直接UI操作ができます。
SurfaceViewクラスにRunnableインタフェースを実装してThreadクラスを使用します。
ただし、プログラム終了時にスレッドを終了させないと終了後のスレッドが生き残ってしまうので 注意が必要です。 - Viewクラスでは、前の画面がクリアされた後に画面が描画されることに対し、SurfaceViewは、前の画面 が残ったままになります。
SurfaceViewサンプル
左隅から45°に移動し、画面端に来たら、90°反転する
SurfaceViewEx
res/drawable/sample.png
sample.png(右クリックで「名前を付けて画像保存」を選択)
SurfaceViewEx.java
import android.app.Activity;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.view.Window;
public class SurfaceViewEx extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
//ウインドウの表示形式
//TRANSLUCENT(半透明)・TRANSPARENT(透過)・OPAQUE(不透明)
getWindow().setFormat(PixelFormat.TRANSLUCENT);
setContentView(new SurfaceViewView(this ));
}
}
SurfaceViewView.java
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
public class SurfaceViewView extends SurfaceView implements Callback, Runnable {
private SurfaceHolder holder;
private Thread thread;
private Bitmap bmp;
private int px = 0; // x座標
private int py = 0; // y座標
private int vx = 10; // x速度
private int vy = 10; // y速度
public SurfaceViewView(Context context) {
super(context);
// 画像の読み込み
Resources r = getResources();
bmp = BitmapFactory.decodeResource(r, R.drawable.sample);
// サーフェイスホルダーの取得
holder = getHolder();
holder.addCallback(this);
}
// サーフェイスの生成
public void surfaceCreated(SurfaceHolder holder) {
thread = new Thread(this);
thread.start();
}
// /SurfaceViewが変化時の処理
public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {
// 画面の大きさや縦横の向きの変更,ピクセルフォーマットの変更時のイベントの処理を記述
}
// サーフェイスの破棄
public void surfaceDestroyed(SurfaceHolder holder) {
thread = null;
}
// スレッドの処理
public void run() {
Canvas canvas;
while (thread != null) {
// ダブルバッファリング
canvas = holder.lockCanvas();
// 前のイメージが残るので一度塗り替える
canvas.drawColor(Color.WHITE);
// イメージの描画
canvas.drawBitmap(bmp, px, py, null);
// ダブルバッファリングのアンロック(Canvasに描画した内容が反映)
holder.unlockCanvasAndPost(canvas);
// 移動
if (px < 0 || getWidth() - bmp.getWidth() < px) {
vx = -vx;
}
if (py < 0 || getHeight() - bmp.getHeight() < py) {
vy = -vy;
}
px += vx;
py += vy;
try {
Thread.sleep(50);
} catch (Exception e) {
}
}
}
}