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()を利用します。
おつかれさまでした。