[Android 開発] グラフィックスの描画 ~ SurfaceView 編

Posted by
ぴろり
Posted at
2015/02/10 10:14
Trackbacks
関連記事 (0)
Post Comment
コメントできます
Category
開発メモ カテゴリ
カバーイメージ

 Android アプリで画面上にグラフィックスを描画する場合、View クラスを使用する方法の他に、SurfaceView クラスを使用する方法があります。SurfaceView クラスを使用した場合、アプリケーションとは別に描画処理のスレッドが独立しており、より高速に安定して再描画が行われます。そのため、ゲームや動画再生などの用途に向いているようです。

この記事を Delicious に追加する   このエントリーをはてなブックマークに追加  

スクラップ帳 » Android アプリ開発 Tips ~ グラフィックスの描画

 Android アプリを開発する際の小技まとめ。画面へのグラフィックス描画などの覚書きを網羅して、必要な機能をコピー&ペーストで再利用できるようにします。

  1. グラフィックスの描画 ~ View 編 2015/02/09
  2. グラフィックスの描画 ~ SurfaceView 編 2015/02/10 今ココ
  3. View でアニメーション描画 2015/02/14

概要

  1. SurfaceView クラスを継承した MySurfaceView を定義します
  2. MySurfaceView は、SurfaceHolder.Callback インタフェースを実装します
  3. MySurfaceView は、アニメーション動作のために Runnable インタフェースを実装します
  4. MySurfaceView について、SurfaceHolder.Callback
  5. の要求する必須のメソッドを実装します。
  6. MySurfaceView について、アニメーション処理を行う run メソッドを実装します
  7. アクテイビティの View に、MySurfaceViewsetContentView します


Fig.1 スクリーンショット

 画面上をボールが跳ね回るアニメーションを作成してみました。スクリーンショットでは 10 フレーム程度の残像しか見えませんが、実機の液晶パネルでは、かなり長い残像が見えます。手元の LG L-05D で動作させたところ、60 FPS で動作していましたので、アクション ゲームなどの高速描画が求められる使途にも十分です。

ソースコード

package com.example.helloworld;

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

public class MainActivity extends ActionBarActivity
{
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView( new MySurfaceView( this ));
    }
}

class MySurfaceView extends SurfaceView
        implements SurfaceHolder.Callback, Runnable
{
    public MySurfaceView(Context context) {
        super(context);

        getHolder().addCallback(this);
    }

    private boolean mIsAttached;
    private Thread mThread;

    public void surfaceCreated(SurfaceHolder holder) {
        mIsAttached = true;
        mThread = new Thread(this);
    }

    private float mScreenWidth, mScreenHeight;

    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        mScreenWidth = width;
        mScreenHeight = height;
        mThread.start();
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        mIsAttached = false;
        while( mThread != null && mThread.isAlive());
        mThread = null;
    }


    public void run() {
        float vx = 5, vy = 5;
        float xOffset = 100, yOffset = 100;
        float radius = 50;

        long now = System.currentTimeMillis(); // FPS 計測用

        while (mIsAttached)
        {
            if (xOffset <= radius || mScreenWidth - radius <= xOffset)
                vx = -vx; // 画面の両端を超えたらバウンドさせる
            if (yOffset <= radius)
                vy = -vy; // 画面の上端を超えたらバウンドさせる
            if (mScreenHeight - radius <= yOffset) {
                yOffset = mScreenHeight - radius;
                vy = -70; // 画面の下端を超えたらバウンドさせる
            }

            xOffset += vx;
            yOffset += vy;
            vy += 5; // 重力加速度

            SurfaceHolder holder = getHolder();
            Canvas canvas = holder.lockCanvas();
            if( canvas != null ) {
                Paint paint = new Paint();
                paint.setColor(Color.GREEN);

                canvas.drawColor(Color.argb(64, 0,0,0));
                canvas.drawCircle(xOffset, yOffset, radius, paint);

                long now_next = System.currentTimeMillis();
                canvas.drawText(String.format("%6.2f", 1000/(float)(now_next - now)), 30, 30, paint);
                now = now_next;

                holder.unlockCanvasAndPost(canvas);
            }
        }
    }
}

解説

26 行目
 SurfaceHolder.Callback として、自身を登録しています。
32~36 行目
 SurfaceView が生成されたタイミングで、描画用スレッド(mThread)を生成して、スレッドを開始しています。
40~43 行目
 生成された SurfaceView のピクセルフォーマットや描画エリアのサイズが変化(確定)したら呼ばれます。
45~49 行目
 SurfaceView が破棄されるタイミングで呼ばれます。描画用スレッド(mThread)を終了するためにフラグ(mIsAttached)を下して、スレッドが終了するのを待っています。
52~90 行目
 画面内をボールが跳ね回るアニメーション処理です。アプリケーションとは別スレッドで動作しているため、コンテクスト切替えなどを意識する必要なく while でグルグル回しています。
74~75 行目
 SurfaceHolder を取得してから、描画用の Canvas を取得しています。取得した Canvas は、表示されている画面とは別のオフ スクリーン バッファとなっており、排他ロックされています。
76 行目
 多くのサンプルコードでは、取得した Canvas の妥当性チェックが省略されていて、アプリケーションを終了する際に、SurfaceView が破棄されたタイミングと、描画スレッドの動作タイミングに依っては、アプリケーションの終了時にエラー メッセージが表示されることがあります。
83~85 行目
 確認のため、FPS 値*1を求めて表示しています。
87 行目
 SurfaceHolder から取得した Canvas のロックを解除します。このタイミングで Canvas の内容が画面に反映されます。

参考リンク

この記事を Delicious に追加する   このエントリーをはてなブックマークに追加  

  1. *1 1 秒間に表示したフレーム数

この記事を読んだ人はこんな記事も読んでいます記事リコメンデーションについて

カバー画像:[Android 開発] グラフィックスの描画 ~ View 編

関連記事/トラックバック

関連記事/トラックバックはまだありません

この記事にトラックバックを送るには?

コメントを投稿する

 
 (必須, 匿名可, 公開, トリップが使えます)
 (必須, 匿名可, 非公開, Gravatar に対応しています)
 (必須)
スパム コメント防止のため「投稿確認」欄に ランダムな数字 CAPTCHAについて を入力してから送信してください。お手数ですがご協力のほど宜しくお願いいたします。