【Android】SurfaceViewを使う

投稿者: | 2011年6月25日

SurfaceViewを使えば、高速な描画を行うことができる。
この前に作成した、自前でアニメーションをする処理や、ゲームなどの描画にはSurfaceViewを使った方が良いだろう。
SurfaceViewのプログラミングはViewのみで描画するよりも、少しだけ複雑になる。
以下にSurfaceViewのプログラミングの例を示す。

MySurfaceView.java

public class MySurfaceView extends SurfaceView {

    private ScheduledExecutorService ses = null;

    private final SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {

            ses.shutdown();
            ses = null;

            Toast.makeText(getContext(), "surfaceDestroyed", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void surfaceCreated(final SurfaceHolder holder) {

            Toast.makeText(getContext(), "surfaceCreated", Toast.LENGTH_SHORT).show();

            ses = Executors.newSingleThreadScheduledExecutor();
            ses.scheduleAtFixedRate(new Runnable() {

                private final int ox = 100;
                private final int oy = 100;
                private final int r = 50;

                private int count = 0;

                private final Paint txtPaint = new Paint();
                private final Paint figPaint = new Paint();

                {
                    txtPaint.setAntiAlias(true);
                    txtPaint.setTextSize(12);

                    figPaint.setAntiAlias(true);
                    figPaint.setStyle(Style.STROKE);
                }

                @Override
                public void run() {

                    final Canvas canvas = holder.lockCanvas();
                    if (canvas != null)
                    {
                        // 描画する
                        canvas.drawColor(Color.WHITE);
                        canvas.drawText( 
               "" + count++, 0, txtPaint.getTextSize(), txtPaint);

                        final double radians = Math.toRadians(count % 360);
                        final int cx = (int)(ox + r * Math.cos(radians));
                        final int cy = (int)(oy + r * Math.sin(radians));

                        canvas.drawLine(ox, oy, cx, cy, figPaint);
                        canvas.drawOval(
                                new RectF(ox - r , oy - r, ox + r, oy + r),
                                figPaint);

                        holder.unlockCanvasAndPost(canvas);
                    }

                }
            }, 0, 16, TimeUnit.MILLISECONDS );
        }

        @Override
        public void surfaceChanged(
       SurfaceHolder holder, int format, int width, int height) {
            // TODO Auto-generated method stub
            Toast.makeText(getContext(), "surfaceChanged", Toast.LENGTH_SHORT).show();
        }
    };

    /**
     * コンストラクタ
     * @param context
     */
    public MySurfaceView(Context context) {
        super(context);

        // コールバックの登録
        getHolder().addCallback(callback);
    }
}

ポイントは、まずSurfaceViewを継承することである。
そして、getHolder()メソッドでSurfaceHolderを取得し、そこにコールバックを登録しているところである。サンプルでは83行目がその処理にあたる。
このコールバックはサーフェイスが作られたときや破壊されるときに呼ばれるので、そのタイミングで描画処理の初期化/終了処理を行う。
描画処理は別にスレッドを立てている。例ではExecutorsを使ってスレッドを取得しているが、もちろん普通のThreadクラスでかまわない。

そして、43行目からのRunnableのrun()メソッド内で、スレッドから直接Viewに描画する。
このときに必要なのはSurfaceHolderのlockCanvas()メソッドでロックされたcanvasを取得し、それに対して描画を行って、unlockCanvasAndPost()メソッドでロックを解除することである。この基本的な流れが分かれば、描画のプログラミング自体は普通のViewのものと変わらないので、色々と応用できるだろう。

Activityのプログラムは、単に上で作った自分のSurfaceViewを登録しているだけである。

public class MainActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new MySurfaceView(this));
    }
}

サンプルは、円の中を直線が回転するプログラムである。
実行結果は以下のようになる。

[slmplayer uri=”2011/06/20110625_device.wmv”]

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です