Android はグラフィックスエンジンに、カスタム 2D グラフィックスライブラリおよびハイパフォーマンス 3D グラフィックスの OpenGL ES 1.0 を備えています。最も共通的な 2D グラフィックス API は drawable パッケージ に多く見られます。 OpenGL API は Khronos OpenGL ES パッケージ から、さらにいくつかの Android OpenGL ユーティリティ が利用できます。
プロジェクトを開始するときは、グラフィカルな要望がどうなるかということを厳密に検討することが重要となります。 さまざまなグラフィックのための作業手順が、さまざまなテクニックでベストなかたちでとしてでき上がります。例えば、静的なアプリケーションに比べ、インタラクティブなゲームや 3D レンダリングのためのグラフィックスやアニメーションの方がまったく違う実装方法でなければなりません。
ここでは、Android のグラフィックス描画のために用意されているオプション、およびそれに対する適切な作業手順について説明していきます。
特に 3D グラフィックスの描画についての情報を探している場合は、このページだけでは十分に役に立てません。しかしながら、下の Canvas を使用した描画 ( および ”SurfaceView の上で” のセクション ) にある情報では、View の階層に描画する方法に関するアイディアの要点が書かれています。Android の 3D グラフィックユーティリティ (OpenGL ES API より提供) に関するさらに詳しい情報は、 OpenGL による 3D およびその他の OpenGL ドキュメントを読んでください。
オプションの考慮
2D グラフィックスを描画する場合、通常以下のふたつのうちのいずれかを行います。
- レイアウトのビューオブジェクトにグラフィックスまたはアニメーションを描画します。この方法では、グラフィックス ( およびアニメーション ) の描画はシステムの通常のビュー階層の描画プロセスによりハンドリングされます。つまり単純にビュー内部の描画すべき場所にグラフィックスを配置するだけです。
- Canvasに直接グラフィックスを描画します。この方法は、個別に適切なクラスの
draw()メソッド ( それに Canvas を渡します ) または、 Canvas のdraw...()メソッドを呼び出し (drawPicture()のように ) ます。 こうすることで、どのようなアニメーションでも制御できるようになります。
オプション "a" は、 View への描画で、動的に変更する必要がなく、パフォーマンス重視のゲームの一部でない単純なグラフィックスの描画を行いたい場合なら最適な選択でしょう。例えば、静的なグラフィックスを View 内に描画したい場合や、静的なアプリケーション内であらかじめ定義されたアニメーションを表示したい場合は、View にグラフィックスを描画すべきです。 View 内部のシンプルなグラフィックス を読んでください。
オプション "b" は、Canvas への描画で、定期的に自身を再描画する必要があるアプリケーションに最適です。基本的には、どのビデオゲームでも Canvas に自身のグラフィックスを描画する必要があります。しかしながら、この方法はただひとつではりありません。
- UI アクティビティと同じスレッド内でレイアウトのカスタムビューコンポーネントを作成し、
invalidate()を呼び出した後、onDraw()コールバックでハンドリングします。 - または、分離したスレッド内で SurfaceView を管理し、スレッドの最大限のスピードを利用して、Canvas に対して描画を行います (
invalidate()の要求を出す必要はありません ) 。
...まずは、 Canvas での描画 を読むことから始めてください。
View 内部のシンプルなグラフィックス
単純なグラフィックス( イメージ、外形、色、定義済みアニメーション、その他 ) を描画することになったとしたら、おそらく View の背景またはレイアウトの ImageView コンテントに単純に描画するだけになると思います。このケースは、このドキュメントのここから先はスキップし、 2D グラフィックス ドキュメントで、グラフィックスとアニメーションを描画する方法について学んでください。
Canvas を使用した描画
特殊な描画を行い、かつ ( または ) アニメーションのグラフィックスを制御するようなアプリケーションを創りたい場合は、 Canvas を介して描画することでそれを実現すべきでしょう。Canvas は実際の面の上に見かけ上存在し、またインターフェイスとして機能しているもので、すべての "draw" 呼び出しを掌握し、その上にグラフィックスを描画します。Canvas を用いて、その下層にある Bitmap ( これはウィンドウ内に配置されています ) の上に実際の描画が行われます。
onDraw() コールバックメソッド内の描画を行っているイベント内で、Canvas は描画の呼び出しを行う必要がある場所のみを提供してくれます。Canvas は、SurfaceView オブジェクトとやり取りを行う場合は、 SurfaceHolder.lockCanvas() からも取得が可能です。 (これら両方のシナリオについては 後のセクションで説明しています ) 。しかしながら、新たな Canvas を作成する必要がある場合は、 Bitmap を定義する必要があり、その上に実際の描画が行われます。Canvas には Bitmap が常に必要です。新しい Canvas は以下のようにしてセットアップできます。
Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
これで Canvas は定義された Bitmap の上で描画ができるようになります。Canvas を使用してその上に描画を行った後は、 Canvas.drawBitmap(Bitmap,...) メソッドのいずれかを使用して、その Bitmap を別の Canvas に持ち運ぶことができます。最終的に最後のグラフィックスの描画は、Canvas が提供している View.onDraw() または SurfaceHolder.lockCanvas() で行うことが推奨されています。 ( 後のセクションを参照 ) 。
Canvas クラス自身には、ユーザ呼び出し可能な、drawBitmap(...)、 drawRect(...)、 drawText(...)、 その他多くの描画メソッドがセットとしてあります。他のクラスにもユーザが呼び出せる draw() メソッドがあります。例えば、Canvas 上にいくつかの Drawable オブジェクトを配置したりこともあるでしょう。Drawable には Canvas を引数として受け付ける draw() メソッド があります。
View で
アプリケーションがかなりの処理能力やフレームレートを要求しない ( チェスゲーム、スネークゲーム、また別のゆっくり動くアニメーションのアプリケーションといった ) 場合は、カスタム View コンポーネントを作成し、Canvas での View.onDraw() で描画することを考えたほうが良いでしょう。とても都合の良く解釈をするなら、Android フレームワークが描画の呼び出しを行う場所となる定義済みの Canvas を提供してくれているところにあります。
始めに、View クラス ( またはその派生 ) を拡張 し、onDraw() コールバックメソッドを定義します。このメソッドは自身のView の描画を要求するために、 Android フレームワークにより呼び出されます。ここが、 Canvas ( onDraw() コールバックで渡されたもの ) を介して描画するための呼び出しを行う場所になります。
Android フレームワークは、必要に応じて onDraw() を呼び出すだけです。アプリケーションで描画の準備ができたタイミングで、毎回 invalidate() を呼び出し、View が無効化されるように要求する必要があります。これは、View が描画されることを望んでいて、Android がそれを受け onDraw() メソッドを呼び出すということを意図しています ( が、このコールバックが即座に呼び出されるという保証はありません ) 。
View コンポーネントの onDraw() の内部では、さまざまな Canvas.draw...() メソッドを使った描画のためだけの Canvas を使用するか、Canvas を引数として受け取る他のクラスの draw() メソッドを使用するかしてください。 onDraw() が完了した後は、Android フレームワークはシステムによりハンドリングされた Bitmap を描画するために、その Canvas を使用するようになります。
注意: メインのアクティビティのスレッドではないスレッドから無効化 ( invalidate ) を要求するには、 postInvalidate() を呼び出す必要があります。
View クラスを拡張するためのガイドとして カスタムコンポーネントの構築 、およびリソースからのイメージや他の基本的な外形といった Drawable オブジェクトの使用に関する情報については、 2D グラフィックス: Drawables を読んでください。
サンプルアプリケーションとして、SDK サンプルフォルダの <your-sdk-directory>/samples/Snake/ にあるスネークゲームを参照してください。
SurfaceView で
SurfaceView は、View の階層内での面を専門に描画する View クラスの特殊なサブクラスです。アプリケーションの2番目のスレッドに面の描画を依頼することにより、アプリケーションがシステムの View 階層が描画の準備できるまで待たされることがなくなというのがこのクラスのねらいです。そのかわり、SerfaceView に対するリファレンスを保持した2番目のスレッドが、自身の Canvas に対する描画を自分のペースで行えるようになります。
まず、 SurfaceView を拡張した新しいクラスを作成する必要があります。また、このクラスは SurfaceHolder.Callback を実装する必要があります。 このサブクラスは Surface に関連する、作成、変更、破棄などの情報を通知するインターフェイスです。これらのイベントは重要であり、それにより描画を開始するのはいつなのか、新たな面のプロパティ上での調整は必要なのか、描画を止めいくつかのタスクを完全に停止するのはいつなのか、ということを知ることができるのです。 SurfaceView クラスの内部も、2番目のスレッドクラス ( これはCanvas に対しすべての描画の処理を実行します ) を定義するには良い場所です。
Surface オブジェクトを直接ハンドリングする代わりに、SurfaceHolder を使ってそれをハンドリングする必要があります。SurfaceView が初期化されたとき、 getHolder() を呼び出すことにより SurfaceHolder を取得します。その後、SurfaceHolder のコールバック ( SurfaceHolder.Callback からの) を addCallback() ( このメソッドに そのコールバックを渡して ) を呼び出すことにより受信したいという旨を、SurfaceHolder に通知する必要があります。その後、SerfaceView クラス内部の SurfaceHolder.Callback の各メソッドをオーバーライドします。
2番目のスレッド内から Surface Canvas に描画するには、SurfaceHandler にそのスレッドを渡し、lockCanvas() でCanvas を取得する必要があります。ここで、SurfaceHolder により与えられた Canvas を受け取り、その上に必要な描画が行えるようになります。Canvas を使用して描画を行った後、そのCanvas オブジェクトを渡し、 unlockCanvasAndPost() を呼び出します。 これでその面がそこに残したいグラフィックを描画するようになります。 この Canvas のロック、アンロックのシーケンスを再描画したいタイミングで毎回実行します。
注意: SurfaceHolder から Canvas の取得処理を行うたびに、Canvas の前の状態が保持されたままになっています。適切にグラフィックを動かすには、面全体を再描画する必要があります。例えば、 drawColor() である色に塗りつぶしたり、背景のイメージを drawBitmap() で設定したりして、Canvas の前の状態をクリアすることができます。 そのしないと、それ以前に実行した描画の痕跡が残ってしまいます。
サンプルアプリケーションとして、SDK サンプルフォルダの <your-sdk-directory>/samples/LunarLander/ にある Lunar Landar ゲームを参照してください。または、 Sample コード セクションのソースに目を通してください。