このサイトでは、分析、カスタマイズされたコンテンツ、および広告に Cookie を使用します。このサイトを引き続き閲覧すると、Cookie の使用に同意するものと見なされます。
Hi, Developers,
straightapps.com ロゴ
作成 June 29, 2022
トップページ > Android 開発トップ > Android Studio で画像を自由に描画する
line
Android 開発
line

ここでは、Android Studio で画像を自由に描画するためのコードについて、書いています。

Canvas だけでもアプリはできる!

ノート PC に、Visual Studio Community 2022 の C++ によるモバイル開発機能のインストールと、 その時点で最新だった Android Studio のインストールを完了しましたので、 Android アプリの開発環境が整ったということになりました。

この記事の前の 「Android Studio で文字を自由に描画する」 では、独自のビューを作成して Empty Activity に設定し、 描画関数 onDrawCanvas を受け取り、文字の書き込みまでできました。

ここではあらかじめ用意した png 画像Bitmap クラス に読み込み、その画像を Canvas に描画するコードを中心として、書いています。

また、タッチイベントの取得についても書いています。

Android Studio で文字を自由に描画する」 で作成したコードへの追記の形となっています。

過去すでに Android Studio を別のマシンで使用して 「素因数分解トレーニングアプリ」 を開発していますが、改めてちゃんとアプリを作りたいな、と思い、リスタートしています。

なお、使用している Android Studio は、2022 年 5 月中旬にインストールした、2021.2.1 Patch 1 の Chipmunk です。

android studio chipmunk

▼ セクション一覧

描画したい画像を用意する
画像を Canvas に描画する
タッチで描画位置を変える
画像を拡大・縮小する

なお、本サイトの ご利用に際しては、必ずプライバシーポリシー(免責事項等)をご参照ください。

描画したい画像を用意する

投稿 June 29, 2022

まずは画面に描画したい画像データを用意します。

今はテスト用に、124 x 125 ピクセル、25 KB 程度の透過あり PNG 画像を用意しました。 下のイメージで、白い部分が透過になっています。

PNG 画像

試しに用意したのは透過のある PNG 画像ですが、 Windows で言えば「ペイント」で保存した透過のない PNG や、写真などの JPG 画像でもいいようです。 逆に Windows で使う、基本的には圧縮されていない BMP 画像は使えないようです(未確認ですが)。

ただ、Android デベロッパーの日本語ページ 「ビットマップの処理」 にあるように、大きなサイズの画像だったり、やたらたくさんメモリに読み込むと 「アプリで使用できるすべてのメモリをすぐに使い果たしてしまいます」 という結果になりますから、注意が必要ということです。

Android Studio でプロジェクトを開いている状態で、 画面左端にある「Resource Manager」を選びます。

Resource Manager

ちなみに今まで、app 以下のフォルダ構成やファイルが表示されていたのは「Project」が選ばれた状態でしたので、 次回起動時などにこの部分が表示されなくなってしまった場合は、 「Resource Manager」の上にある「Project」をクリックして開けば元通りです。

Drawable が選択状態になっているその下、FlashSosuu.app.main と書かれていますが、 これはプロジェクト作成時に FlashSosuu という名前を付けているためです。 別の名前でプロジェクトを作成した場合は、その名前で表示されます。

Drawable」が選ばれていることを確認したら、 エクスプローラーで、ここ Drawable ( 描画(draw)できる(able)データを入れるフォルダです。 ) にコピーしたい画像ファイルを左ボタンのドラッグでドロップします。 このやり方が一番簡単みたいです。

すると、Import drawables という画面が表示されました。

Import drawables

表示されている様子から、一度に複数の画像(リソース)を取り込むことができそうです。 複数のリソースをドロップして、うっかり間違えて不要なものもドロップしてしまった場合は、 その欄の右上にある「Do not import」で、それだけをキャンセルできそうです。

good.png と書かれているのは、画像ファイル名です。 ドロップされたファイル名がそのまま表示されますが、その上の部分で自由に別の名前にできるようです。

その右、drawable はインポート先(または種類)で、画像リソースを入れる場所です。

気になる QUALIFIER TYPE と書かれたところは、プロパティの設定のような感じです。 選択すると、値が設定されていれば VALUE に表示され、そうでなければ新しく登録(選択)できそうです。 今は特に変更しなくていいと思うので、このまま画面下部の Next をクリックします。

Import drawables (2)

確認画面です。

自動判別されたのか、標準的な割り振りなのかわかりませんが、 前の画面、QUALIFIER TYPEDensity、つまり密度を選ぶと Medium になっていましたので、 中密度用の drawable-mdpi が選ばれているようです。 右側、画像の下にも「Medium Density」と表示されています。 今は深く追求しませんので、とりあえずはこれで OK です。

デバイスの解像度別にファイルを用意したい場合は、drawable にあるそれぞれの解像度用のフォルダに同名でファイルを置くことになると思いますが、今はやりません。 確認したら、Import をクリックします。

Resource Manager

インポートに成功すると、リストに追加されました。

なお、プロジェクトに登録されたこの画像は、プロジェクトの res フォルダにコピーされたものですので、オリジナルは削除されません(移動されたわけではありません)。 また、あとでオリジナルを編集しても、プロジェクトには反映されません(ショートカットではない)。

Explorer

エクスプローラーでプロジェクトのパスを見ると、app、src、main、res と進んだ drawable-mdpi にコピーされたことを確認できます。

これで画像データの用意ができましたので、描画コードに進みます。

▲ページ先頭へ

画像を Canvas に描画する

投稿 June 29, 2022

画面に表示させたい画像(リソース)の登録が完了しましたので、 画面右側のコード FlashSosuuView.java に戻ります。 もちろんファイル名は、独自のビューの作成時につけた名前になっています。

すでに閉じてしまった場合は、画面左端の Project を選んで、 app → java → パッケージ名にある FlashSosuuView.java のようなビュー名をダブルクリックすれば、開けます。

コンストラクタの前で、画像リソースを Bitmap クラスbmp に読み込んでいます。

この場合、FlashSosuuView クラスが生きているうちは bmp が確保され続けますので、 Android デベロッパーの日本語ページ 「ビットマップの処理」 のように、慎重に対応する必要がありますが、今は気にしません。

ちなみに Java では明示的にメモリを解放する方法はないようですので、不要になったら null を設定してクリーンしてもらう、 という方法になるようですが、一般的ではないようです。

decodeResource

コンストラクタの前に、次の文を加えました。

	private Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.good);

BitmapFactorydecodeResource 関数static 関数で、Bitmap を返します。

decodeResource 関数の最初の引数 getResources 関数は、 リソースのおおもとを表す Resources を返す、Context の関数です。 現時点の知識では言い方が正しいかはわかりませんが、Context は自分自身 this なので、記述するときは this を省略して書かれている、ということになるでしょう。

decodeResource 関数の 2 つ目の引数で渡している R はリソースのルート、 drawable は drawable フォルダ以下にある画像データを表し、 そこにあるリソース名が インテリセンス ( この言葉はマイクロソフト用語でしょうか? 入力アシスタントとか、サポートツールとか言うのでしょうか? ) で表示されますので、インポートしておいたリソース名、ここでは good を選択しています。

Bitmap の部分が、クラスの import 文を追加していないため赤文字になっていますので、 マウスでポイントし、出てきたヒントにある Import class を選択して、import 文を自動追加してもらいます。

続いて、描画のための関数 onDraw に、画像を読み込んだ bmp の描画コードを追加します。

onDraw

drawColor 関数で背景をクリアしたあとに、次のコードを加えました。

	canvas.drawBitmap(bmp, (float)0, (float)0, paint);

drawBitmap 関数に渡している引数は、 まず始めに画像データを保持している Bitmap クラス(のオブジェクト)です。

続く 2 つは、描画位置で左上の座標実数型 float で渡しています。 単位はピクセルになっているようですので、解像度や画面サイズが異なるデバイスに対応するための工夫が必要になります。

これで画面に描画されるはずですので、実機を接続して実行してみます。

実機画面

ちゃんと(利用可能な領域の)左上に表示されました。 ちょっと離れているように見えますが、これは透過ビットが周囲を取り囲んでいるためです。

画面の中央に表示されるように、画面サイズと画像サイズを計算してみます。

drawBitmap

drawBitmap の部分を、このように書き換えました。

	canvas.drawBitmap(bmp,
			(float)(cxView - bmp.getWidth()) / 2,
			(float)(cyView - bmp.getHeight()) / 2, paint);

bmp.getWidth 関数で bmp に読み込まれている画像の幅を、 bmp.getHeight 関数で bmp に読み込まれている画像の高さを、ピクセル単位で取得しています。

実機画面

座標指定を改良すると、画面中央に表示できました。

テキスト描画!」と書かれた部分の底辺の y 座標が画面の中央ですので、 それが円の中心に来ていますから、計算も良さそうです。

この計算であれば、どのような解像度のデバイスでも、画面中央に表示されることになります。

実機画面

drawBitmap 関数をもう 1 つ追加して、 座標を左上にずらして表示させてみると、正しく透過されている様子も確認できました。

ちなみに実機を接続しているとき、Android 側でアプリを終了させれば Android Studio 側も停止状態になりますが、 Android Studio の右上の赤い四角ボタンでアプリを終了させると、Android 端末側も終了したように見えて、 アプリがバックグラウンドに残ってしまうようです。 それで問題ないのかわかりませんが、ちゃんと終了してからテストを続けたいと思います。

次は、画面タッチと描画を連動させてみます。

▲ページ先頭へ

タッチで描画位置を変える

投稿 June 29, 2022

onTouchEvent 関数に、座標関連の処理を実装します。

この記事の前の 「Android Studio で文字を自由に描画する」 で、すでに onTouchEvent 関数のオーバーライドを作成し、 event.getAction() 呼び出しで得られた値で分岐できるようになっていて、 マウスの左ボタンダウンに相当する MotionEvent.ACTION_DOWN の分岐を用意しました。

変数の定義

まずはタッチ位置を記録するため、 コンストラクタの前で(関数定義の外側で)、 float 型cxTouchcyTouch を定義しました。 もちろん変数名は何でも構いませんし、m_cxTouch と書いたり、touchX と書いたり、fTouchX と書いたり、いろいろな変数名の付け方があるようです。 本当は最初から Android の流儀にあった名前付けが良いとは思います。

また、画像の表示状態として、nShow を用意し、 0 のときは画像非表示、それ以外の時は表示と想定します。 初期値 0 を設定しています。 ちなみに、この命名はおそらく Windows の C/C++ 独自 ( 最初の n が int 型を表し、そのあとに大文字はじめの名前を付けています。 ) ですので、本当は Android の流儀にあった名前付けが良いとは思います。

onTouchEvent

タッチ処理関数 onTouchEventMotionEvent.ACTION_DOWN の分岐にコードを追加します。 画面に手を触れていない状態から触れた(タッチされた)ときに、この処理が実行されます。

event.getX 関数と、event.getY 関数で、タッチされた座標を取得しています。 float 型であるものの、ピクセル単位の座標が返されているようです。

続いて表示制御用の変数 nShow に 1 を入れ、onDraw 関数の次の呼び出しで画像が表示されるようにします。

このとき、画面の書き換えを促すため、invalidate 関数を呼び出して、画面を無効化します。 画面が無効であるとき、Android は画面描画のため、描画コードを呼び出すようです。 Windows の C/C++ で言えば、InvalidateRect で画面を無効化すると、WM_PAINT メッセージがポストされる、というイメージとまったく同じと言えます。

余談ですが、validate は英語の動詞「有効にする」であって、商業施設の駐車場の無料サービスを受けるのに「バリデーション」という言い方をする場合があると思います。 それに in が付いていますから否定されて、「無効化」です。 validate という英単語がわからないと、覚えるのが大変そうです。

描画コードも書き換え、タッチした位置が中心となるように、画像を描画するようにします。

onDraw

描画関数 onDraw のコードも書き換え、タッチした位置が中心となるように、画像を描画するようにします。 先ほど単体で drawBitmap していた部分を、上のように書き換えました。 座標計算は drawBitmap 内に書いても同じですが、見にくくなりましたので分離しました。

実機で起動すると、最初は画像は表示されませんが、 画面をタッチすると、その位置を中心として、画像が描画されます。

実機画面

今はタッチされたとき(タッチされていない状態から画面に触れた状態になったとき)のみ、 座標を取得し画面を無効化して再描画させていますので、 画面にタッチして指を動かしても、追従してきてはくれません

onTouchEvent

このように、移動を意味する MotionEvent.ACTION_MOVE でも座標を取得すれば、追従してきてくれます。

なお、super.onTouchEvent 関数の呼び出しは、ACTION_MOVE のあとに呼び出すと影響があるようでしたので、 アプリが処理しなかった場合のみ呼び出すこととし、処理した場合は true を返すようにしています。

実機画面

さまざまな解像度のデバイスに対応させるためにはまだ拡大や縮小を行っての描画が必要ですが、 とりあえずは画像も描画できるようになり、かなり自由度が増したと言えます。

▲ページ先頭へ

画像を拡大・縮小する

投稿 June 29, 2022

ここまでである程度自由に画面に描画できるようになりましたから、 接続している Android 端末の画面を構成できることになります。

タッチされた位置もわかるようになりましたし、画面解像度もわかっていますから、 画面に配置したパーツにタッチされたかどうかを判定することも可能になりました。

ただ、Android 端末にはさまざまな画面解像度がありますから、 縦に長かったり、スクエアに近かったり、高密度だったり低密度だったり、 タブレットのように大画面だったり、ミニサイズだったりで、アプリは工夫が必要です。 工夫がなければ、画面から構成要素がはみ出して見えなくなったり操作不能になったり、 あるいは小さく表示されて扱いにくくなったり見づらくなったりします。

cxViewcyView に画面解像度を取得できていますから、 例えば画面にボタン 4 つを横に並べたい、という場合の 1 つのボタンの領域は計算できます。

それに対して画像イメージをたくさん持っておくのは不可能ですから、拡大・縮小が必要です。

今回使ったものとは別の引数をとる drawBitmap 関数を使えば、拡大・縮小が簡単にできます。 Android Developers のドキュメントは、 こちら です。

drawBitmap

第 2 引数の src は bmp のどこを使うかの指定ですが、null、つまり「指定なし」を指定すれば、画像全体が使われることになります。

第 3 引数の dst は destination、「行き先」で、ここに Rect、つまり四角の座標を渡せばいいことになります。

onDraw

onDraw 関数の画像を描画しているコードのあと、テキストを描画するコードの前に、追加してみます。

まずは描画領域を定義する Rect 型の dst を定義します。 Rect が赤文字になっていますので、マウスポインタを乗せ、Import class を選んで、import 文を追加してもらいます。

縮小するケースと拡大するケースのコードを追加します。

onDraw

dst に、new Rect を設定して、指定の値で初期化しています。 引数は、左、上、右、下の座標を指定できますので、この場合、(50, 50)-(100, 100) の範囲、50 x 50 のサイズに縮小することになります。

次の行、drawBitmap 関数には、src に null を渡していますので bmp に入っている画像全体が、dst に描画されることになります。

同じように、dst.set 関数で、すでにある dst の値を 0, 0, cxView, cxView に書き換えています。 つまり、左上 (0, 0) から画面右端 cxView まで、そして正方形を保って cxView の y までを指定しています。 元画像が 124 x 125 でしたので、cxView x cxView のサイズに拡大されることになります。

実機画面

明らかに画質が落ちていますから、無謀な拡大・縮小と言えるでしょう。 同じ画像を複数サイズで用意しておいて、 欲しいサイズに近い画像データを使うようにしないといけませんね。

ちなみに正しく drawable のサブフォルダに入れておけば、Android 側が適切に選択してくれると思います。

▲ページ先頭へ
line
関連トピックス
line

Canvas だけでもアプリはできる! #1
Android Studio で文字を自由に描画する

アプリ独自のビューを作成し、テキストを描画するまでのコードを作成しています。


Canvas だけでもアプリはできる! #3
Android Studio でタイマーによる自動画面更新

MainActivity にタイマーを設定して、一定間隔で処理を呼び出すコードを作成しています。

Canvas だけでもアプリはできる! #4
Android Studio で効果音を鳴らす

効果音のような短い音を鳴らすコードを作成しています。

Canvas だけでもアプリはできる! #5
Android Studio で画面を準備する

アクションバーの消去、画面の縦固定指定、背景画像描画、そしてタッチでウェブサイトを開くコードを実装しています。

Canvas だけでもアプリはできる! #6
Android Studio でボタン表示&入力

座標管理クラスを作成して画面に適切なサイズのボタンを表示し、どれが押されたか判断するタッチ処理を実装しています。 トースト表示や独自クラスの作成についても書いています。

Canvas だけでもアプリはできる! #7
Android Studio でゲームクラスを更新

正解のボタンをタッチしたとき効果音を鳴らす等の正解処理を行い、2 秒後に次の問題に自動的に進む処理を実装しています。 不正解ならトーストで理由を表示します。


Android Studio で全画面プロジェクトを作成する

Empty Activity では画面上部に邪魔になり得るタイトル表示領域がありますが、 Fullscreen Ativity なら、それがなくなるのか、新規プロジェクトを作成して確認しています。

Android Studio で ActionBar を非表示にする

Empty Activity では画面上部に邪魔になり得るタイトル表示領域がありますが、 プロジェクト内の設定の書き換えにより、別プロジェクトにしなくても非表示にできるようです。

Android Studio で Native C++ プロジェクトを作成する

目的としている C++ ネイティブコードを併用する Activity は、 Native C++ プロジェクトを作成すれば、比較的簡単に着手できそうです。

Android Studio で AdMob プロジェクトを作成する

もうひとつの目的としては、AdMob でアプリ内に広告を入れる、です。 AdMob Ads Activity なら簡単に実装できるのでしょうか?

アプリケーション名を指定する

C++ ネイティブアプリでアプリ名を設定する方法の詳細を検討しています。

line
その他のおすすめ
line

Android 開発に関する記事をまとめた Android 開発トップ もご覧ください。

素因数分解トレーニング

以降の数学の基本となる素因数分解の基本部分を、ひたすらトレーニングするための JavaScript コードについて書いています。

因数分解トレーニング(1)

因数分解の公式の一部について、ひたすらトレーニングするための JavaScript コードについて書いています。



© 2017-2022 StraightApps.com 無断転載を禁じます。No reproduction without permission.