SurfaceView を使用すれば、高速なグラフィックス描画が可能なことが判りましたが、View でも同じことができないか試してみました。
結論から言えば、「動作はするが、なめらかアニメーションの描画には向かない」ということが判りました。
View クラスを継承して、MyView クラスを宣言しますMyView クラスは、Runnable インタフェースを実装しますMyView#onDraw を override して、グラフィックスを描画をしますMyView#run を implement して、画面を更新し続けます
手順 4 において、単純に invalidate() を呼び出すとエラーになります。MyView の描画は、自分自身のメイン スレッドからしか行えないためで、そのままでは MyView#run で走っている別スレッドからは MyView の描画を更新できません。そこで、Handler でメッセージ キューを作成して、メイン スレッド宛てに描画するようメッセージを送信しています。
package com.example.helloworld;
import android.support.v7.app.ActionBarActivity;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
public class MainActivity extends ActionBarActivity
{
private Thread mThread;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView( new MyView( this ));
}
public void onDestroy(){
super.onDestroy();
mThread.interrupt();
}
private class MyView extends View implements Runnable
{
private Handler mHandler;
public MyView( Context context ) {
super( context );
mHandler = new Handler();
mThread = new Thread(this);
mThread.start();
}
private Paint mPaint = null;
private float mScreenWidth, mScreenHeight;
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged( w,h, oldw,oldh );
mPaint = new Paint();
mScreenWidth = w;
mScreenHeight = h;
}
float vx = 5, vy = 5;
float xOffset = 100, yOffset = 100;
float radius = 50;
long now = System.currentTimeMillis(); // FPS 計測用
protected void onDraw( Canvas canvas ) {
super.onDraw(canvas);
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;
mPaint.setColor( Color.GREEN );
canvas.drawColor(Color.argb(64, 0,0,0));
canvas.drawCircle(xOffset, yOffset, radius, mPaint);
long now_next = System.currentTimeMillis();
canvas.drawText(String.format("%6.2f", 1000/(float)(now_next - now)), 30, yOffset, mPaint);
now = now_next;
}
public void run() {
while( true ) {
mHandler.post(new Runnable() {
public void run() {
invalidate();
}
});
}
}
}
}
Handler オブジェクトを生成している。キューは、このスレッド(描画スレッド)にバインドされている。Runnable インスタンスをポストしている。これによって、描画スレッドで invalidate() が実行されて、画面が更新される。
FPS 値が一桁ということもなく、SurfaceView を使用した場合と遜色なく高速に動作しますが、ただ、不意に動作がカクカクすることがあって、なめらかなアニメーション動作を安定して続けることができません。上記のソースコードでは、MyView.onDraw 内で canvas に直接描画していますが、オフ スクリーン バッファに描画してから、最後に canvas に書き出すようにしたところ、更にカクつきが酷くなりました。テーブルゲームなどのアクション性を要求されない描画では十分ですが、「不意にカクつく」という点はゲームなどでは許容できないこともありますので、やはりそこは SurfaceView を使った方が良いようです。