XMLレイアウトでSurfaceViewを用いる - Android

XMLレイアウトでSurfaceViewを用いる手順を紹介します。

概要

こちらの記事では、コードでSurfaceViewを生成しましたが、画面の一部にSurfaceViewを配置する場合はXMLレイアウトを用いたほうが便利です。この記事ではXMLレイアウトを用いた場合にSurfaceViewを画面に配置するコードを紹介します。

プロジェクトの作成

Android アプリケーションプロジェクトを作成します。
[New Android Application]ダイアログボックスが表示されますので、以下を設定します。
  • Application Name: "SimpleSurfaceViewXML"
  • Project Name: "SimpleSurfaceViewXML"
  • Package Name: "com.iPentec.simplesurfaceviewxml"
  • Minimum Required SDK: "API 8: Android 2.2 (Froyo)"
  • Target SDK: "API 17: Android 4.2 (Jelly Bean)"
  • Compile With: "API 17: Android 4.2 (Jelly Bean)"
  • Theme: "Holo Light with Dark Action Bar"

UIの作成

レイアウトを変更します。
レイアウトXMLファイルを編集して下記のコードを記述します。
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/TableLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <SurfaceView
        android:id="@+id/SurfaceViewMain"
        android:layout_width="fill_parent"
        android:layout_height="200dp">
    </SurfaceView>
    
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

</TableLayout>

デザイナの画面では数の状態になります。

コード

下記のコードを記述します。
MainActivity.java
package com.ipentec.simplesurfaceviewxml;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.SurfaceView;
import android.widget.Button;

public class MainActivity extends Activity {
  private SurfaceView surfaceView;    
  private MainSurfaceView mainSurfaceView;
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    surfaceView = (SurfaceView)findViewById(R.id.SurfaceViewMain);
    mainSurfaceView = new MainSurfaceView(this, surfaceView);
  }

  @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;
  }
}

解説

onCreateメソッド内の
  surfaceView = (SurfaceView)findViewById(R.id.SurfaceViewMain);
  mainSurfaceView = new MainSurfaceView(this, surfaceView);
が追記部分となります。1行目はSurfaceViewのリソースIDを用いてfindViewById()メソッドを呼び出し、SurfaceViewオブジェクトを取得します。2行目はカスタム化したMainSurfaceViewのインスタンスを作成します。MainSurfaceViewのコンストラクタの第二引数にSurfaceViewのオブジェクトを与えます。
MainSurfaceView.java
package com.ipentec.simplesurfaceviewxml;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Paint;

public class MainSurfaceView implements SurfaceHolder.Callback, Runnable {
  private SurfaceHolder holder;   
  private static final String TAG = "MainSurfaceView";
  private Thread thread;

  public MainSurfaceView(Context context, SurfaceView sv) {
    holder = sv.getHolder();
    holder.addCallback(this);
  }
  @Override
  public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
    Log.d(TAG, "surfaceChanged");
  }
  
  @Override
  public void surfaceCreated(SurfaceHolder arg0) {
    Log.d(TAG, "surfaceCreated");
    Canvas canvas = arg0.lockCanvas();
    canvas.drawColor(Color.BLACK);
    arg0.unlockCanvasAndPost(canvas);
  }
  @Override
  public void surfaceDestroyed(SurfaceHolder arg0) {
    Log.d(TAG, "surfaceDestroyed");
    thread=null;
  }
  
  @Override
  public void run() {
    Log.d(TAG, "run");
  }
}

解説

SurfaceHolder.Callback, Runnableを継承したクラスの場合コンストラクタ、surfaceChanged, surfaceCreated, surfaceDestroyed, runのメソッドのオーバーライドが必要なため、各メソッドを実装しています。

実行結果

プロジェクトを実行します。数の画面が表示されます。まだ何も描画されない状態です。

描画のコードを記述する

画面描画のコードを記述します。MainSurfaceView.javaを下記のコードを記述します。
MainSurfaceView.java
package com.ipentec.simplesurfaceviewxml;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Paint;

public class MainSurfaceView implements SurfaceHolder.Callback, Runnable {
  private SurfaceHolder holder;   
  private static final String TAG = "MainSurfaceView";
  private Thread thread;

  public MainSurfaceView(Context context, SurfaceView sv) {
    holder = sv.getHolder();
    holder.addCallback(this);
  }

  @Override
  public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
    Log.d(TAG, "surfaceChanged");
    
    thread = new Thread(this);
    thread.start();
  }
  
  @Override
  public void surfaceCreated(SurfaceHolder arg0) {
    Log.d(TAG, "surfaceCreated");
    Canvas canvas = arg0.lockCanvas();
    canvas.drawColor(Color.BLACK);
    arg0.unlockCanvasAndPost(canvas);
  }
  @Override
  public void surfaceDestroyed(SurfaceHolder arg0) {
    Log.d(TAG, "surfaceDestroyed");
    thread=null;
  }
  
  @Override
  public void run() {
    Log.d(TAG, "run");
    while (thread != null) {
      doDraw(holder);
    }
  }
  
  private void doDraw(SurfaceHolder holder) {
    Canvas canvas = holder.lockCanvas();
    if (canvas != null){
      Paint paint = new Paint();
      paint.setColor(Color.YELLOW);
      canvas.drawColor(Color.BLACK);
      
      Path path = new Path();
      float theta = (float)(Math.PI * 72 / 180);
      float r = 50f;
      PointF center = new PointF(200f, 80f);
      float dx1 = (float)(r*Math.sin(theta));
      float dx2 = (float)(r*Math.sin(2*theta));
      float dy1 = (float)(r*Math.cos(theta));
      float dy2 = (float)(r*Math.cos(2*theta));
      path.moveTo(center.x, center.y-r);
      path.lineTo(center.x-dx2, center.y-dy2);
      path.lineTo(center.x+dx1, center.y-dy1);
      path.lineTo(center.x-dx1, center.y-dy1);
      path.lineTo(center.x+dx2, center.y-dy2);
      path.lineTo(center.x, center.y-r);
      canvas.drawPath(path, paint);

      holder.unlockCanvasAndPost(canvas);
    }
  }
}

解説

スレッドの作成と解放

スレッドの作成と解放のコードは下記になります。
surfaceChangedメソッドに
  thread = new Thread(this);
  thread.start();
を追記します。

また、surfaceDestroyedメソッドに
  thread=null
を追記します。

描画

下記が描画部のコードになります。
runメソッドに
  while (thread != null) {
    doDraw(holder);
  }
を記述します。

doDrawメソッドを記述します。キャンバスをロックし、星形を描画するコードを記述します。
private void doDraw(SurfaceHolder holder) {
  Canvas canvas = holder.lockCanvas();
  if (canvas != null){
    Paint paint = new Paint();
    paint.setColor(Color.YELLOW);
    canvas.drawColor(Color.BLACK);
      
    Path path = new Path();
    float theta = (float)(Math.PI * 72 / 180);
    float r = 50f;
    PointF center = new PointF(200f, 80f);
    float dx1 = (float)(r*Math.sin(theta));
    float dx2 = (float)(r*Math.sin(2*theta));
    float dy1 = (float)(r*Math.cos(theta));
    float dy2 = (float)(r*Math.cos(2*theta));
    path.moveTo(center.x, center.y-r);
    path.lineTo(center.x-dx2, center.y-dy2);
    path.lineTo(center.x+dx1, center.y-dy1);
    path.lineTo(center.x-dx1, center.y-dy1);
    path.lineTo(center.x+dx2, center.y-dy2);
    path.lineTo(center.x, center.y-r);
    canvas.drawPath(path, paint);

    holder.unlockCanvasAndPost(canvas);
  }
}

実行結果

プロジェクトを実行します。星形が描画されました。

著者
iPentecのプログラマー、最近はAIの積極的な活用にも取り組み中。
とっても恥ずかしがり。
最終更新日: 2024-01-04
作成日: 2013-04-05
iPentec all rights reserverd.