描画用スレッドを実装できる SurfaceView の利用方法

Android の画面表示の基礎は View です。 ウィジェットなども View を継承しています (なので View そのものです。 is-a 関係)。

アクティビティに画面を繋げるには setContentView を呼び出しますね。

さて、高度なアニメーションを必要とする場合などは描画面 (Drawing Surface) を自分で極力制御したい。だけどそれが、 View じゃないと、 アクティビティと関連付けができません。

ということで、出てくるのが SurfaceView です。

・・・って、説明がちょっと強引だったでしょうか(苦笑)

簡単に技術的に言えば、描画用のスレッドを持った View を作るときは、SurfaceView を使います

OpenGL でも GLSurfaceView というのがあって専用の描画用スレッドを持っていますが、ここで説明すのは、 基本的な SurfaceView についてです。

専用スレッドを持つ SurfaceView の実装方法

さっそく SurfaceView の実装例を説明します。

package com.keicode.android.test;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class AnimationSurfaceView extends SurfaceView 
  implements Runnable, SurfaceHolder.Callback {

  static final long FPS = 20;
  static final long FRAME_TIME = 1000 / FPS;
  static final int BALL_R = 30;
  SurfaceHolder surfaceHolder;
  Thread thread;
  int cx = BALL_R, cy = BALL_R;
  int screen_width, screen_height;

  public AnimationSurfaceView(Context context) {
    super(context);
    surfaceHolder = getHolder();
    surfaceHolder.addCallback(this);    
  }

  @Override
  public void run() {
        
    Canvas canvas = null;
    Paint paint = new Paint();
    Paint bgPaint = new Paint();
    
    // Background
    bgPaint.setStyle(Style.FILL);
    bgPaint.setColor(Color.WHITE);
    // Ball
    paint.setStyle(Style.FILL);
    paint.setColor(Color.BLUE);
        
    long loopCount = 0;
    long waitTime = 0;
    long startTime = System.currentTimeMillis();

    while(thread != null){

      try{
        loopCount++;
        canvas = surfaceHolder.lockCanvas();

        canvas.drawRect(
          0, 0, 
          screen_width, screen_height,
          bgPaint);
        canvas.drawCircle(
          cx++, cy++, BALL_R, 
          paint);

        surfaceHolder.unlockCanvasAndPost(canvas);

        waitTime = (loopCount * FRAME_TIME) 
          - (System.currentTimeMillis() - startTime);

        if( waitTime > 0 ){
          Thread.sleep(waitTime);
        }
      }
      catch(Exception e){}
    }

  }

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

  @Override
  public void surfaceCreated(SurfaceHolder holder) {
    thread = new Thread(this);
    thread.start();
  }

  @Override
  public void surfaceDestroyed(SurfaceHolder holder) {
    thread = null;
  }
  
}

Surface へアクセスするために、SurfaceHolder が用意されています。SurfaceView の getHolder で SurfaceView オブジェクトがとれます。

SurfaceHolder を通じて Surface の変化、例えばサーフェスが作られた、縦横が変わったなどがあったときのコールバックを設定できます。

ここではコールバックの設定、及び、スレッドのタスク (Runnable オブジェクト) をすべて、SurfaceView でまとめて実装しています。

コンストラクタで SurfaceHolder のコールバックオブジェクトの設定を行います。こうすると Surface が作られた、 変更された、破棄されたというタイミングで、surfaceCreated、surfaceChanged、そして surfaceDestroyed が呼ばれます。

実際の描画処理は、run メソッド内で行っています。Surface の canvas をとってきて、そこに対して描画すれば OK です。

Activity 側は特に何も変わったことはありません。SurfaceView のインスタンスを作って、setContentView を呼ぶだけです。

package com.keicode.android.test;

import android.app.Activity;
import android.os.Bundle;

public class AnimationTest1Activity extends Activity {
  
  AnimationSurfaceView surfaceView;
  
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        surfaceView = new AnimationSurfaceView(this);
        setContentView(surfaceView);
        
    }
}

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Android 開発入門