SurfaceViewのコード記述方法について - SurfaceViewのコールバックを別クラスにする - Android

SurfaceViewのコードの記述方法についての紹介です。

SurfaceViewの基本コード

SurfaceViewの基本コードは以下になります。

MySurfaceView.java

package com.iPentec.simplesurfaceview;

import android.content.Context;
import android.util.AttributeSet;
import android.view.*;
import android.view.SurfaceHolder.Callback;

/**
 * TODO: document your custom view class.
 */
public class MySurfaceView extends SurfaceView implements Callback{
  public MySurfaceView(Context context) {
    super(context);
  }

  public MySurfaceView(Context context, AttributeSet attrs) {
    super(context, attrs);
  }
	
  public MySurfaceView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
  }

  @Override
  public void surfaceCreated(SurfaceHolder arg0){

  }

  @Override
  public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3){

  }
	
  @Override
  public void surfaceDestroyed(SurfaceHolder arg0){

  }
}

コード記述方法 その1 (SurfaceViewのコールバックをSurfaceViewのクラスとする)

画面描画やViewの作成や廃棄のコールバックをSurfaceViewのクラス自身で受け取る方式のコード記述です。

UI

MainActivityにMySurfaceViewを配置します。

コード

以下のコードを記述します。

MySurfaceView.java

package com.iPentec.simplesurfaceview;

import android.content.Context;
import android.util.AttributeSet;
import android.view.*;
import android.view.SurfaceHolder.Callback;
import android.graphics.*;

/**
 * TODO: document your custom view class.
 */
public class MySurfaceView extends SurfaceView implements Callback {
  private SurfaceHolder holder = null;

  public MySurfaceView(Context context) {
    super(context);
    initSurface();
  }

  public MySurfaceView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initSurface();
  }

  public MySurfaceView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    initSurface();
  }

  @Override
  public void surfaceCreated(SurfaceHolder arg0) {
    drawSurface();
  }

  @Override
  public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {

  }

  @Override
  public void surfaceDestroyed(SurfaceHolder arg0) {

  }

  public void initSurface() {
    holder = this.getHolder();
    holder.addCallback(this);
  }

  public void drawSurface() {
    Canvas canvas = holder.lockCanvas();
    canvas.drawColor(Color.WHITE);
    Paint paint = new Paint();
    paint.setColor(Color.GREEN);
    canvas.drawRect(100, 100, 150, 150, paint);
    holder.unlockCanvasAndPost(canvas);
  }

}
解説
このコードではMySurfaceViewのクラス自身がSurfaceViewのコールバックを受け取るため、MySurfaceViewクラスにコールバックを受け取るためのCallback (正確には SurfaceHolder.Callback)インターフェイスをimplementsで実装します。
SurfaceHolder.Callbackインターフェイスを実装したクラスはsurfaceCreated, surfaceChanged, surfaceDestroyedメソッドをオーバーライドする必要があります。オーバーライドされたメソッドがSurfaceViewからのコールバックとして呼び出されます。SurfaceViewの初期化時にはSurfaceHolderオブジェクトのaddCallback()メソッドを呼び出しコールバック先のコールバッククラスを指定する必要があります。上記のコードではMySurfaceViewクラス自身がコールバックを受けるため、initSurfaceメソッド内で
  holder.addCallback(this);

と"this"を引数に与え自分自身をコールバック先として指定しています。

MainActivity.java

package com.iPentec.simplesurfaceview;

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

public class MainActivity extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
  }
}

実行結果

プロジェクトを実行します。下図の画面が実行結果として表示されます。

コード記述方法 その2 (SurfaceViewのコールバックを匿名クラスとする)

画面描画やViewの作成や廃棄のコールバックをSurfaceViewのクラス内部で定義、作成した匿名クラスで受け取る方式のコード記述です。

UI

MainActivityにMySurfaceViewを配置します。

コード

以下のコードを記述します。MySurfaceView.javaはパターンA、パターンBのどちらでも同じ動作になります。

MySurfaceView.java (パターンA)

package com.iPentec.simplesurfaceview2;

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

public class MySurfaceView extends SurfaceView{
  Paint p;
  
  public MySurfaceView(Context context, AttributeSet attrs, int defStyle)
  {
    super(context, attrs, defStyle);
    init();
  }
  
  public MySurfaceView(Context context, AttributeSet attrs)
  {
    super(context, attrs);
    init();
  }

  public MySurfaceView(Context context)
  {
    super(context);
    init();
  }
  
  private void init(){
    p = new Paint();
    p.setColor(Color.RED);
    
    SurfaceHolder surfaceHolder = getHolder();
    surfaceHolder.addCallback(
        new SurfaceHolder.Callback() {
          
          @Override
          public void surfaceDestroyed(SurfaceHolder holder) {
            // TODO Auto-generated method stub
          }
          
          @Override
          public void surfaceCreated(SurfaceHolder holder) {
            // TODO Auto-generated method stub
            draw(holder);
          }
          
          @Override
          public void surfaceChanged(SurfaceHolder holder, int format, int width,
              int height) {
            // TODO Auto-generated method stub
          }
        }
    );
  }
  
  private void draw(SurfaceHolder holder){
    Canvas canvas = holder.lockCanvas();
    canvas.drawColor(Color.GRAY);
    canvas.drawCircle(100, 100, 25, p);
    holder.unlockCanvasAndPost(canvas);
  }
}

MySurfaceView.java (パターンB)

package com.iPentec.simplesurfaceview2;

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

public class MySurfaceView extends SurfaceView{
  Paint p;
  
  public MySurfaceView(Context context, AttributeSet attrs, int defStyle)
  {
    super(context, attrs, defStyle);
    init();
  }
  
  public MySurfaceView(Context context, AttributeSet attrs)
  {
    super(context, attrs);
    init();
  }

  public MySurfaceView(Context context)
  {
    super(context);
    init();
  }
  
  private void init(){
    p = new Paint();
    p.setColor(Color.RED);
    
    SurfaceHolder surfaceHolder = getHolder();
    
    SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
      
      @Override
      public void surfaceDestroyed(SurfaceHolder holder) {
        // TODO Auto-generated method stub
      }
      
      @Override
      public void surfaceCreated(SurfaceHolder holder) {
        // TODO Auto-generated method stub
        draw(holder);
      }
      
      @Override
      public void surfaceChanged(SurfaceHolder holder, int format, int width,
          int height) {
        // TODO Auto-generated method stub
      }
    };
    
    surfaceHolder.addCallback(callback);
  }
  
  private void draw(SurfaceHolder holder){
    Canvas canvas = holder.lockCanvas();
    canvas.drawColor(Color.GRAY);
    canvas.drawCircle(100, 100, 25, p);
    holder.unlockCanvasAndPost(canvas);
  }
}
解説
このコードではMySurfaceView内に記述した匿名クラスがSurfaceViewのコールバックを受け取ります。コード中の"new SurfaceHolder.Callback()"の後の"{"~"}"部分に匿名クラスの実装を記述します。
パターンAではSurfaceHolder.addCallbackの引数内に匿名クラスのインスタンスを直接与えています。バターンBではSurfaceHolder.Callbackの変数にインスタンスを作成し、その変数をSurfaceHolder.addCallback()の引数に与えています。
コールバックは匿名クラスのsurfaceDestroyed, surfaceCreated, surfaceChangedメソッドが呼び出されます。

MainActivity.java

package com.iPentec.simplesurfaceview2;

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

public class MySurfaceView extends SurfaceView{
  Paint p;
  
  public MySurfaceView(Context context, AttributeSet attrs, int defStyle)
  {
    super(context, attrs, defStyle);
    init();
  }
  
  public MySurfaceView(Context context, AttributeSet attrs)
  {
    super(context, attrs);
    init();
  }

  public MySurfaceView(Context context)
  {
    super(context);
    init();
  }
  
  private void init(){
    p = new Paint();
    p.setColor(Color.RED);
    
    SurfaceHolder surfaceHolder = getHolder();
    
    MySurfaceViewCallback msvc = new MySurfaceViewCallback();
    surfaceHolder.addCallback(msvc);
  } 
}

実行結果

プロジェクトを実行します。下図の画面が実行結果として表示されます。

コード記述方法 その3 (SurfaceViewのコールバックを別のクラスで受ける)

画面描画やViewの作成や廃棄のコールバックを別のクラスで受け取る方式のコード記述です。

UI

MainActivityにMySurfaceViewを配置します。

コード

以下のコードを記述します。

MySurfaceView.java

package com.iPentec.simplesurfaceview2;

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

public class MySurfaceView extends SurfaceView{
  Paint p;
  
  public MySurfaceView(Context context, AttributeSet attrs, int defStyle)
  {
    super(context, attrs, defStyle);
    init();
  }
  
  public MySurfaceView(Context context, AttributeSet attrs)
  {
    super(context, attrs);
    init();
  }

  public MySurfaceView(Context context)
  {
    super(context);
    init();
  }
  
  private void init(){
    p = new Paint();
    p.setColor(Color.RED);
    
    SurfaceHolder surfaceHolder = getHolder();
    MySurfaceViewCallback msvc = new MySurfaceViewCallback();
    surfaceHolder.addCallback(msvc);
  }
}
解説
このコードではSurfaceViewのコールバックは下記のMySurfaceViewCallbackクラスのメソッドで受けます。MySurfaceViewlクラスでは、MySurfaceViewCallbackクラスのインスタンスを作成し、SurfaceViewのSurfaceHolderのaddCallbackメソッドを呼び出して、MySurfaceViewCallbackのインスタンスを与えてMySurfaceViewCallbackクラスをコールバック先に指定します。

MySurfaceViewCallback.java

package com.iPentec.simplesurfaceview2;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.SurfaceHolder;

public class MySurfaceViewCallback implements SurfaceHolder.Callback {
  Paint p;
  
  public MySurfaceViewCallback(){
    p = new Paint();
    p.setColor(Color.BLUE);
  }

  @Override
  public void surfaceDestroyed(SurfaceHolder holder) {
    // TODO Auto-generated method stub
  }
  
  @Override
  public void surfaceCreated(SurfaceHolder holder) {
    // TODO Auto-generated method stub
    draw(holder);
  }
  
  @Override
  public void surfaceChanged(SurfaceHolder holder, int format, int width,
      int height) {
    // TODO Auto-generated method stub
  }
  
  private void draw(SurfaceHolder holder){
    Canvas canvas = holder.lockCanvas();
    canvas.drawColor(Color.GRAY);
    canvas.drawRect(150, 200, 200, 250, p);
    holder.unlockCanvasAndPost(canvas);
  }
}
解説
SurfaceViewのコールバックを受け取るクラスです。コールバックを受け取るクラスはSurfaceHolder.Callback インターフェイスを実装する必要があるため、"implements SurfaceHolder.Callback"を記述します。
surfaceDestroyed, surfaceChanged, surfaceChangedがSurfaceViewの画面作成や画面描画の際に呼び出されるコールバックメソッドになります。

MainActivity.java

package com.iPentec.simplesurfaceview2;

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

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }  
}

実行結果

プロジェクトを実行します。下図の画面が実行結果として表示されます。

補足

SurfaceView単体ではマルチスレッドはサポートされていないため、draw()メソッドを下記のコードにした場合、アプリケーションがロックします。ロックしないようにするには描画処理を別スレッドにします。描画部分を別スレッドにするコードはこちらの記事を参照してください。
  private void draw(SurfaceHolder holder){
    int cx=10;
    int cy=10;
    
    while (true){
      cx++;
      cy++;
      
      Canvas canvas = holder.lockCanvas();
      canvas.drawColor(Color.GRAY);
      canvas.drawCircle(cx, cy, 25, p);
      holder.unlockCanvasAndPost(canvas);
    }
  }
著者
iPentecのプログラマー、最近はAIの積極的な活用にも取り組み中。
とっても恥ずかしがり。
最終更新日: 2024-01-04
作成日: 2013-05-16
iPentec all rights reserverd.