スレッドを用いたアニメーション処理サンプル
今回紹介するサンプルを実行させると、次のような青い線の円が、時計のように円を描いてアニメーションするというものです。Androidでアニメーションを行うには、いくつかの方法がありますが、今回はThreadクラスを用いて処理します。
独自のビュー
サンプルのソースコードは、2つのクラスから構成されています。ひとつは、Activityクラスを継承したHelloAndroidクラス、もうひとつは、そのHelloAndroidクラス内で定義している、Viewクラスを継承したDrawViewSampleクラスです。
HelloAndroidクラスでは全体的な処理、DrawViewSampleクラスではスレッドを用いたグラフィック描画処理(円の表示)を記述しています。
スレッド部分の説明の前に、まずは、HelloAndroidクラス全体を見てみましょう。
public class HelloAndroid extends Activity { // 独自のビュー DrawViewSample v; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); v = new DrawViewSample(getApplication()); setContentView(v); } @Override protected void onResume() { super.onResume(); v.onResume(); // スレッド生成、開始の呼び出し } @Override protected void onPause() { super.onPause(); v.onPause(); // スレッド終了の呼び出し } class DrawViewSample extends View { ~中略~ } }
今回のサンプルでは、描画処理をすべてソースコードで記述できるように、ビューをXMLから生成するのではなく、動的に定義して生成しています。それがDrawViewSampleクラスです。
HelloAndroidクラスでオーバライドしているonResume、onPauseメソッドは、第2回目に説明したように、自動的に呼ばれるメソッドです。onResumeメソッドは、Activityが表示される直前、onPauseメソッドは、Activityが最前列でなくなったときに呼ばれます。
一般にonPauseメソッドでは、変更情報の保存やアニメーションの停止処理を記述します。なおActivityが再び前面に表示されると、またonResumeメソッドが呼ばれます。
サンプルコードでは、それぞれのタイミングで、スレッドの生成、開始と、終了の呼び出しを行っています。
グラフィックの描画
Androidでのグラフィックを描画する基本的な考え方は、AWTやSwingを使ったデスクトップのJavaアプリケーションと同じです。つまり、画面の表示を更新する際に呼び出されるメソッドがあり、そのなかで描画処理を記述します。Androidでは、ViewクラスのonDrawメソッドが、そのメソッドに相当します。
次のコードは、DrawViewSampleクラスから、onDrawメソッドの部分だけを抜粋したものです。
class DrawViewSample extends View { @Override protected void onDraw(Canvas canvas) { // 描き方の指定 Paint paint = new Paint(); paint.setColor(Color.BLUE); paint.setStrokeWidth(2); paint.setAntiAlias(true); paint.setStyle(Paint.Style.STROKE); // 座標の設定 double r = toRadians(6 * this.ix); double x = 100 + 50 * cos(r); double y = 100 + 50 * sin(r); // 線で円を描く canvas.drawCircle((float)x, (float)y, 5, paint); } }
onDrawメソッドでは、Canvasクラスのオブジェクトが渡されます。Canvasオブジェクトとは、いわゆるグラフィックコンテキストで、描画のためのメソッドが多数用意されています。サンプルの円の描画は、CanvasクラスのdrawCircleメソッドを利用しています。drawCircleメソッドは、引数として、座標、半径、描き方を指定します。
Paintクラスは、図形の描き方に関する設定を行います。ここでは、円の色、線の幅、線のスタイルなどを指定しています。
座標の設定では、前述したMathクラスの三角関数を用いて、円弧の座標を求めています。
スレッド処理
次に示すコードは、DrawViewSampleクラスの残りの処理、つまりスレッド処理の部分です。
class DrawViewSample extends View { int status = 0; // 状態管理変数 int ix = 0; Thread timer; // (1)スレッドとして実行したい処理の定義 Runnable task = new Runnable() { public void run() { // statusが0の間実行する while (status == 0) { ix++; // 座標更新のためインクリメント postInvalidate(); // 画面更新依頼 try { Thread.sleep(100); // 100ミリ秒間待機 } catch (InterruptedException e) { } } } }; // (2)スレッドの開始 public void onResume() { timer = new Thread(task); // Threadクラスのインスタンス化 timer.start(); // スレッドの開始 } // (3)スレッドの終了 public void onPause() { status = 1; // スレッドを終了させる try { timer.join(); // スレッドが終了するまで待機 } catch (InterruptedException e) { } } }
(1)では、無名クラスを用いてRunnableインタフェースを実装しています。(2)は、Threadクラスのインスタンス化と、スレッドの開始、(3)は、スレッドの終了処理で、Threadクラスのjoinメソッドを使って、スレッドが終わるまでアプリケーションを待機させています。なお今回の例では、このjoinメソッドでの待機を省いても問題ありません。ただし、スレッドが終わる前にアプリケーション本体が終了してしまう、といった事態を防ぐために、通常joinメソッドで待機するようにします。
スレッドの内容
スレッドとして実行している処理は、実質として、ix++とpostInvalidateメソッドの呼びだしの2行だけです。次のsleepメソッドは、処理の間隔をあけるために、スレッドを待機させます。
変数ixのインクリメントは、円弧の座標を進めるためです。postInvalidateメソッドとは、Viewクラスで定義されたメソッドで、画面の再描画をリクエストします。画面を無効にし、描き直すようにAndroidに伝えるのです。このメソッドを受けて、Androidでは再描画処理を行い、onDrawメソッドが呼ばれます。onDrawメソッドで描画するたびに、円の座標が進んでいるため、動いているように見えるというわけです。
ただこの場合の再描画は、即座に行われるわけではありません。そのため、サンプルのアニメーションも若干ギクシャクした動きになっているはずです。このあたりの仕組みは、また回をあらためて説明することにしましょう。
最後に
今回は、java.langパッケージを中心に、スレッドとグラフィックの基本処理を解説しました。次回は、ジェネリクスやコレクションクラスを中心に紹介することにします。
参考資料
- 『Javaポケットリファレンス』 WINGSプロジェクト 高江賢 著、山田祥寛 監修、2011年3月、技術評論社
- Java Platform, Standard Edition 6 API 仕様
- Android Developers