2015年3月9日月曜日

Androidで画像を動かす SurfaceViewの使い方

目次へ



  • SurfaceViewとは
  • SurfaceViewを使って画像を動かす例
  • 画像をタッチしたらカウントしてみる


■■■■SurfaceViewとは■■■■

SurfaceViewが他のViewと異なるのはメインスレッド以外のスレッドから描画ができるという点です。
SurfaceView以外では、メインスレッドで描画処理をしなければならないため、定期的に再描画を繰り返すようなものには向きません。 その点、SurfaceViewは別のスレッドを作成し、その中で、ずっと再描画を繰り返すということができます。


■■■■SurfaceViewを使って画像を動かす例■■■■

  • まずは、SurfaceViewを継承したクラスを作成します。下の例のMyViewがそれです。
    コンストラクタでは、あとで表示をする時に使うために、リソースからBitmapオブジェクトを取り出しておきます①。

  • 表示用スレッドをスタートさせるのは画面が表示された時です。②はそのための処理です。
    画面が表示された時に呼び出されるのは、SurfaceHolder.CallbackインターフェースのsurfaceCreatedメソッドです。
    SurfaceHolder.Callbackインターフェースには、surfaceCreated⑤、surfaceChanged⑥、surfaceDestroyed⑦が定義されています。
    これらのメソッドをこのViewに登録するのが②です。

  • DrawThreadクラスは画像を表示するためのスレッドです。
    その中で、表示用のいろいろなメソッドを持つCanvasオブジェクトを取り出しているのが③です。
    このCanvasは他のスレッドからの描画を排他するので、lockCanvasというメソッド名となっています。
    そのため、描画が終わったら他のスレッドからの描画ができるようにするための④が必要です

  • 画像を表示するmydrawメソッドは、canvasを使ってランダムな位置に画像を表示しているだけです。
    このメソッドがスレッドから、2秒おきにずっと呼び出されます


MyViewクラス
import java.util.Random;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;

public class MyView extends SurfaceView implements Callback {
    Random random = new Random();
    Bitmap bitmap;  //表示画像
    int x;   //表示位置x
    int y;   //表示位置y
    int bitmapWidth; //画像サイズ 幅
    int bitmapHeight; //画像サイズ 高さ

    Thread thread = null; //画像を表示するスレッド

    public MyView(Context context) {
        super(context);
        bitmap = BitmapFactory.decodeResource
                       (getResources(), R.drawable.ic_launcher);     //①
        bitmapWidth = bitmap.getWidth();
        bitmapHeight = bitmap.getHeight();
        getHolder().addCallback(this);                               //②
    }
    //画像を表示するスレッド
    private class DrawThread extends Thread {
        public void run() {
            SurfaceHolder holder = getHolder();
            while(true) {
                Canvas canvas = holder.lockCanvas();                 //③
                if (canvas != null) {
                    mydraw(canvas);
                    holder.unlockCanvasAndPost(canvas);              //④
                }
                try {
                    sleep(2000);
                } catch (InterruptedException e) {}
            }
        }
    }
    //画像を表示する
    private void mydraw(Canvas canvas) {
        canvas.drawColor(Color.WHITE);

        int width  = canvas.getWidth() - bitmapWidth;
        int height = canvas.getHeight() - bitmapHeight;

        x = random.nextInt(width);
        y = random.nextInt(height);

        canvas.drawBitmap(bitmap, x, y, new Paint());
    }
    //-----画面が生成された時に呼び出される            ⑤
    public void surfaceCreated(SurfaceHolder holder) {
        thread = new DrawThread();
        thread.start();
    }
    //-----画面のサイズ等が変化した時に呼び出される    ⑥
    public void surfaceChanged(SurfaceHolder holder,
                            int format, int width, int height) {
    }
    //-----画面が削除された時に呼び出される            ⑦
    public void surfaceDestroyed(SurfaceHolder holder) {
       thread = null;
    }
}



このMyViewクラスのインスタンスをActivityクラスのonCreate内のsetContentViewに渡せば表示されます。


Activityクラス
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(new MyView(this));
 }



■■■■画像をタッチしたらカウントしてみる■■■■

上のMyViewクラスのフィールドに、画像をタッチできたらカウントしてその数を表示するようにしてみます。


public class MyView extends SurfaceView implements Callback {
    ------フィールド追加------
    int count = 0; 

    private void mydraw(Canvas canvas) {
        ------最後に次の3行を追加------
        Paint p = new Paint();
        p.setTextSize(35);
        canvas.drawText("タッチできた数"+count, 50, 50, p);
    }

    -------タッチした時に呼び出されるメソッドを追加------
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int cx = (int)event.getX();
        int cy = (int)event.getY();
        if(x <= cx && cx <= x+bitmapWidth && y < cy && cy <= y+bitmapHeight) {
            count++;
        }
        return super.onTouchEvent(event);
    }
}



にほんブログ村 IT技術ブログ IT技術メモへ
にほんブログ村