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()を利用します。

おつかれさまでした。

4 Comments