2011年9月6日火曜日

SurfaceViewで高速描画する(1)

SurfaceViewは高速に描画を行うための仕組みです。
通常のViewでは処理が間に合わない、ゲームやマルチメディアなど高速処理に向いています。
別スレッドで描画するため、資源の排他処理などマルチスレッドを意識する必要があります。

SurfaceViewの特徴は以下の3点+3メソッドです。

画面(Surface)を描画する専用スレッドを提供する(資源ロックが発生)
Surfaceの描画には、SurfaceHolderというインターフェイスを利用
描画処理はSurfaceHolderのコールバックとして実装する
SurfaceHolder.Callback.surfaceCreated()
SurfaceHolder.Callback.surfaceChanged()
SurfaceHolder.Callback.surfaceDestroyed()
TechBoosterでは一度、カメラの使用方法(1)、(2)で触れています。
その際は、Cameraというハードウェアのマルチメディア処理を主眼に紹介しました。
今回は改めて描画処理をおこなうSurfaceViewについて解説します。


SurfaceViewを用意する
SurfaceViewの実装ではSurfaceHolderをインターフェイスとして使うため、
描画用のコールバックも必要になります。まとめて内部クラスとして用意します。

実装すべきメソッドは4つで、非常に簡単に用意できます。

SurfaceViewのコンストラクタ
SurfaceHolder.Callback.surfaceChanged()
SurfaceHolder.Callback.surfaceDestroyed()
SurfaceHolder.Callback.surfaceDestroyed()
今回のサンプルでは、わかりやすさを優先してActivityとViewを分離しました。

sampleSurfaceView.java (SurfaceView側)
public class sampleSurfaceView extends SurfaceView implements SurfaceHolder.Callback{

//コンストラクタ
public sampleSurfaceView(Context context) { ... }

//SurfaceView変更時に呼び出される
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { ... }

//SurfaceView生成時に呼び出される
public void surfaceCreated(SurfaceHolder holder) { ... }

//SurfaceView破棄時に呼び出される
public void surfaceDestroyed(SurfaceHolder holder) { ... }
}
Activity側ではSurfaceViewのインスタンスを生成します。
surfaceViewActivity.java (Activity側)

public class surfaceViewActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new sampleSurfaceView(this));
}
}
画像と文字列を描画する
必要な処理はコールバックの登録と実装です。
SurfaceViewのコンストラクタで登録処理を行います。

private Bitmap mImage;
//コンストラクタ
public sampleSurfaceView(Context context) {
super(context);

getHolder().addCallback(this);
mImage = BitmapFactory.decodeResource(getResources(), R.drawable.bakeneko);

}
6行目のgetHolder()メソッドでSurfaceHolderを取得、さらにコールバックを登録しています。
7行目で今回使うBitmapリソースを用意しています。

注釈:SurfaceViewとは直接関係ありませんが、独自拡張したViewをXmlに記述する場合、
コンストラクタはpublic sampleSurfaceView(Context context)ではなく、
public sampleSurfaceView(Context context, AttributeSet attrs) が呼び出されるので注意してください。

SurfaceViewの描画に必要なSurfaceHolderのコールバック一覧です。
SurfaceHolder.Callback
コールバック名 通知タイミングと役割
surfaceCreated 生成時。初期画面の描画
surfaceChanged 変更時。画面の更新処理
surfaceDestroyed 破棄時。画面の削除、後処理
サンプルでは更新処理が無いため、surfaceCreatedでの描画処理のみ実装しています。

//SurfaceView生成時に呼び出される
public void surfaceCreated(SurfaceHolder holder) {

//初期描画(生成タイミングで描画する必要があるもの)

//Canvasの取得(マルチスレッド環境対応のためLock)
Canvas canvas = holder.lockCanvas();
Paint paint = new Paint();
paint.setTextSize(24);
paint.setColor(Color.WHITE);

//描画処理(Lock中なのでなるべく早く)
canvas.drawBitmap(mImage, 0, 0, paint);
canvas.drawText("TechBooster",0,200,paint);

//LockしたCanvasを解放、ほかの描画処理スレッドがあればそちらに。
holder.unlockCanvasAndPost(canvas);
}

//SurfaceView変更時に呼び出される
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

//SurfaceView破棄時に呼び出される
public void surfaceDestroyed(SurfaceHolder holder) {

}
7行目と17行目でCanvasの排他処理を行っています。マルチスレッド環境では資源の競合を考慮して取得時にLock、利用が終了すればunlockして解放する必要があります。
それぞれ、SurfaceHolderのlockCanvas()、unlockCanvasAndPost()を利用します。

0 件のコメント:

コメントを投稿