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 を使った方が良いようです。