OpenGLに行く前にSurfaceViewを実装してみた

Android + OpenGL ESをちょっと調べてて、そこに行くよりも前にSurfaceViewを扱ったほうが良さそうなことに気がついた。

SurfaceViewというのは、名前からも分かるとおり、Viewのサブクラスです。以前連載第4回の「簡単でワクワクするAndroidウィジェット10連発!」で紹介した「ウィジェット」も、同様にViewのサブクラスですが、それらのウィジェットとSurfaceViewには決定的な違いがあります。

 それは、描画の方式が違うのです。パッケージ「android.widget」に属するウィジェットは、アプリケーションのスレッド内で描画が行われるため、定期的に再描画を繰り返すゲームなどには向いていません。一方、SurfaceViewは、アプリケーションのスレッドと描画処理のスレッドが独立しているため、定期的な再描画に向いています。

 例外的に、android.widget.VideoViewクラスは、SurfaceViewのサブクラスです。「SurfaceViewが動画再生などの負荷の描画処理に向いている」という一例ですね。

SurfaceViewならAndroidで高速描画ゲームが作れる (1/3) - @IT

アニメーションのような「動きのあるもの」を実装するにはSurfaceViewを使わないとダメらしい。ちなみにAndroidOpenGLを叩くViewもGLSurfaceViewというSurfaceViewを継承したクラスになってるので、それだけ重要だということか。

とりあえずSurfaceViewを使って文字列を描画するだけのコードを書いてみた。Viewを継承した場合と比べると、僅かに冗長なコードになる。

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class HelloActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new MySurfaceView(this));
    }
    
    private class MySurfaceView extends SurfaceView {
        public MySurfaceView(Context context) {
            super(context);
            getHolder().addCallback(new SurfaceCallback());
        }

        private class SurfaceCallback implements SurfaceHolder.Callback {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                Canvas canvas = holder.lockCanvas();
                
                // 背景色を白に
                canvas.drawColor(Color.WHITE);
                
                // 文字列を描画
                Paint paint = new Paint();
                paint.setColor(Color.BLACK);
                paint.setAntiAlias(true);
                paint.setTextSize(16);
                canvas.drawText("hoge", 20.0f, 20.0f, paint);
                
                holder.unlockCanvasAndPost(canvas);
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width,
                    int height) {
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
            }
        }
    }
}