Canvas だけでもアプリはできる!
ノート PC に、Visual Studio Community 2022 の C++ によるモバイル開発機能のインストールと、 その時点で最新だった Android Studio のインストールを完了しましたので、 Android アプリの開発環境が整ったということになりました。
「Android Studio で文字を自由に描画する」 で、独自のビューを作成して Empty Activity に設定し文字を描画、 続く「Android Studio で画像を自由に描画する」 で、PNG 画像をプロジェクトに取り込んで、描画関数 onDraw が引数で受け取った Canvas に画像を描画しました。 拡大・縮小もできていますし、画面タッチの検出もできました。
「Android Studio でタイマーによる自動画面更新」ではタイマーをセットし、 一定間隔で独自に作成したビューを無効化して、ユーザーの操作がなくても画面を更新できるようにしました。
前回はクイズで正解したときなどに鳴らす、短い効果音の再生処理を実装しました。 「Android Studio で効果音を鳴らす」に書いています。
ここでは画面準備として、 画面上部のアプリタイトル領域「アクションバー」を非表示にする、 画面を縦固定にする、 背景画像を用意して描画する、 そして画面のタッチからブラウザでウェブサイトを開く、 といった項目を実装しています。
ここまで作成したコードへの追記の形となっていますが、すべて必要と言うわけではありません。
過去すでに Android Studio を別のマシンで使用して 「素因数分解トレーニングアプリ」 を開発していますが、改めてちゃんとアプリを作りたいな、と思い、リスタートしています。
なお、使用している Android Studio は、2022 年 5 月中旬にインストールした、2021.2.1 Patch 1 の Chipmunk です。
なお、本サイトの
ご利用に際しては、必ずプライバシーポリシー(免責事項等)をご参照ください。
投稿 July 18, 2022
まずは画面の上部、アプリのタイトルが表示されている「アクションバー」を非表示にします。 本来はメニューなどを表示するための領域ですが、今はこの領域を使用しませんので、邪魔になっています。
詳しくは、 「Android Studio で ActionBar を非表示にする」 に書いていますので、ここでは作業のみの記述です。
今回は
Android Studio の画面左側、Project の構成リストから、 res、 values、 themes と、順番に開いていきます。
みつかった themes.xml の style タグ、アプリ名が書かれたテーマの定義の部分、 Theme.MaterialComponents.DayNight.DarkActionBar の指定を Theme.MaterialComponents.DayNight.NoActionBar に書き換えます。
これでアクションバーは表示されなくなりました。
もう 1 つのファイル、themes.xml (night) も忘れずに書き換えておきましょう。
投稿 July 18, 2022
新しく作成した Empty Activity などでは、画面を横にすると自動的に横画面に切り替わりますが、画面を構成する際には、面倒に感じます。 最初からスマホアプリのデザイナーであれば当たり前かもしれませんが、 Windows アプリを中心に開発している私としては、2 種類の画面デザインを用意することにはまったく慣れていません。
縦長に画面を使うことをポートレート、 デバイスが倒されて横長になった画面をランドスケープと呼ぶようです。 この切り替えは Android OS によって認識され、アプリに通知が送られて、 特にコードを追加していない場合には、画面の切り替えが行われるようです。
今は何でもかんでも 1 つの独自ビュー内で行おうとしていますので、2 種類のデザインを用意するのが面倒です。 おそらくコードとしてもだいぶ複雑になるのでしょう。
AndroidManifest.xml、つまりアプリへの指示ファイルを編集し、 ポートレート(縦長画面)固定とします。
Android Studio 画面の左側、 Project が選ばれているときに表示される、 app、manifests と開くと見つかる AndroidManifest.xml をダブルクリックして開きます。
application タグ内にある activity タグを見つけます。
android:exported="true" のあと、> の前に、 画面を固定するための指示、 android:screenOrientation="portrait" を追加します。
これで、画面(デバイス)を横にしても、横画面にならなくなります。
"portrait" 指定を "landscape" に変えれば、横画面固定にもできます。
投稿 July 18, 2022
本格的なアプリであれば、さまざまな画面解像度、さまざまな画面密度に対応すべきですが、 単純なアプリであれば、次のような簡易な形でもいいのかな、と考えています。
ここで取っている方法はあくまで簡易的なものですが、もちろん悪くはないと思っています。 ちなみにネイティブアプリで利用される画像処理ライブラリ OpenGL ES の場合は、 画面のサイズに関わらず、x、y 方向ともそれぞれ -1.0 〜 +1.0 で指定する方法となっていますが、 これもまた、最初からそういうシステムの開発者あるいはデザイナーであれば普通なのでしょうが、 Windows デスクトップアプリの開発者としては、多少指定しにくい感じがします。
ですので、以下の方法が正統的というわけではありませんし、 こうすべきとオススメしているわけでもないということを念頭に、参考にしていただければよいかと思います。
このところはもっと画面が長いデバイスが増えているようですが、 ここでは基本の縦横比を 16:9 と仮定して、どのような画面解像度であっても、16:9 の領域を使用することとします。 おそらく大抵は長い方向にあまりが出ると思われますので、センタリングすることにします。 逆に、短い方向にあまりが出る可能性もありますから、その場合はその方向でセンタリングし、 とにかく画面中央部の 16:9 の領域に描画することにします。
座標の与え方は OpenGL ES のように -1.0 〜 +1.0 でもどう指定しても結果は同じですが、 Windows の開発者らしく、左上を (0,0)、右下を (899,1599) とする、1600 x 900 ピクセルの画面のつもりで指定することにします。 もちろん指定した座標を実際に描画に使うときには、実際の画面の座標に変換する必要があります。 つまり、プログラム内では 1600 x 900 系の座標指定を行って、実際には画面にあわされる、というイメージです。
具体的には、実際の画面が 1700 x 900 ピクセルであるなら上下に 50 ピクセルづつスペースを作ってセンタリングします。 実際の画面の解像度が小さく 800 x 450 ピクセルであれば(そんなサイズはもうないでしょうけど)50% に縮小表示します。 タブレットなどの場合 3200 x 1800 ピクセルなら 200%に拡大し、 スクエアに近い 1600 x 1200なら左右 150 ピクセルつづスペースを作ってセンタリング、という感じです。
ひとまず背景画像を表示させるまでを実装しますので、縦 1,600 ピクセル、横 900 ピクセルの画像を作成しました。
アクションバーを消してしまいましたのでアプリ名を表示し、 画面の背景を黒として、上下の部分で違和感がでにくいようにしています。
「Android Studio で画像を自由に描画する」の 「描画したい画像を用意する」のように、 リソースの Drawable に背景画像 flashsosuu_bg.png を登録します。 ちなみに mp3 ファイルの登録時にハイフンが使用できないことがわかりましたが、アンダースコアは使えるようです。
Resource Manager を開いて、Drawable を表示させ、 登録したいファイルを Ctrl キーを押しながらドロップすれば、ファイルがコピーされて登録されることになります。
この背景画像は、アプリ起動中はいつでも使いますので、作成した独自のビュークラス FlashSosuuView で読み込みっぱなしにしておきます。
せっかくなので目標は高く、
アプリが停止状態になったとき(ホーム画面や他のアプリに切り替えられたとき)には画像を解放し、
アプリが再開されたときに、ふたたび画像を読み込むようにしたいと思います。
※ 結果として実装しきれずやめましたので、先にスキップしていただいても構いません。
アプリが再開されたときに呼び出される onResume 関数に、背景画像の読み込みコードを入れましょう。 これはアプリ起動時にも呼び出されるはずですので、ここだけに読み込みコードを書けば済みます。
操作手順としては、独自のビュー FlashSosuuView のコンストラクタと onWindowFocusChanged の定義の間に隙間を作り、 右クリックして「Generate...」、「Override Methods...」と選択し、 「Select Methods to Override/Implement」の画面が表示されたら・・・。
ここではアプリのアクティビティ MainActivity、つまり Context ではなく、 View 派生クラスですので、onResume 関数はありません。 View がどういう順序で呼び出されているかを調べて最適な場所を探すか、 MainActivity の onResume を実装して FlashSosuuView の関数を呼び出す必要がありますね。
(アプリとしての)
そして、描画関数 onDraw で、画面全体に描画するようにします。
onWindowFocusChanged 関数で設定している描画可能領域のサイズ cxView, cyView に加え、 1600 x 900 領域をセンタリングするためのオフセット cxOffset, cyOffset の定義と、 拡大・縮小のためのレート(割合)disp_rate も定義しておきます。 disp_rate については単純に rate でもよさそうですが、あまりに一般的な変数名だと何かと当たる(使われている)といけませんので、気を付けています。
そしてウィンドウが生成されフォーカスを得たときに呼び出される onWindowFocusChanged 関数にコードを追加し、値を設定します。 おそらく大抵は横方向、短辺がいっぱいで縦方向、長辺に余りがでると想定できますので、 短辺をいっぱいにしたときの計算を行い、16:9 の比にして長辺が収まらない場合は、長辺を基準に計算しています。
続いて描画関数 onDraw ですが、まず確認のためにただリソース good を描画しているコードをコメントアウトしました。 /* から */ までがブロックでコメントアウトされます。 ネスト(入れ子)はできませんので、ご注意ください。 なお、ここまでのコードを使用していない場合は、この作業はありません。
そして背景色のクリア(黒に変えました)のあとに、背景描画用のコードを追加しました。
描画コードの追加のあと、画面にタッチされてタイマーで呼び出される関数内にある背景クリアのためのコード drawColor をコメントにしました。
無事に背景画像が表示されました。 まだ試せてはいませんが、どのような解像度のデバイスでも、このようにセンタリングされることになるはずです。
投稿 July 18, 2022
プログラムとしては問題の作成、ボタンの描画、タッチの認識などがまだあり、 アプリの形としてはアイコンの設定などがありますが、それらは次回以降にまわし、 このページでは最後に、単純な「ブラウザでウェブサイトを開く」コードを実装します。
アプリのサイズは小さいほうがユーザーにはありがたいですが、説明画面などは必要です。 「ウェブビュー」を使うと便利なようですが、ネイティブアプリでは使えませんでしたので、 「JNI でウェブサイトを開く」のようにウェブサイトを開くコードを C/C++ で実装しました。 これと同じように、Java で実装します。
アプリから、スマホにインストール済みのブラウザでウェブサイトを開くには、 コードを追加するだけではなく、AndroidManifest.xml に追記が必要です。
AndroidManifest.xml を開き、manifest タグの内側で、 インターネットアクセスのパーミッションを得る、次の一文を追記します。
<uses-permission android:name="android.permission.INTERNET" />
これで AndroidManifest.xml の準備は完了ですので、次はコードです。
タッチイベント処理 onTouchEvent 関数にコードを追加します。
前回までに作成したコードに、処理を追加しました。
タッチ位置が、画面縦方向で、背景画像の上部 cyOffset から(縦 1,600 ピクセルのうち)1,400 ピクセルより下なら追加のコードが実行され、 指定のウェブサイトを開けるようにしました。 正確には、画面が縦に長い場合、1,400 ピクセルより下、背景画像がない部分でも同じ動作となります。
とりあえずコードをコピー&ペーストすると、クラスの定義等で赤文字になりました。
Uri は Java クラスですから、マウスポインタをのせて「Import class」をクリックすれば、 自動的に import 文が挿入され、解決されます。 Intent も Java クラスですから、同じように、マウスポインタをのせて「Import class」で解決できます。
startActivity は View の関数ではありませんので、Import class で解決することはできません。
マウスポインタを乗せると 「FlashSusuuView にある startActivity 関数を解決できません」 と表示され、 「FlashSosuuView に startActivity 関数を作成する」かどうかを尋ねられます。 startActivity 関数でブラウザを起動しようとしていますので、View に関数を定義しても、何の解決にもなりません。
View クラス の getContext 関数 で、 Context、 つまり MainActivity を取得し、その startActivity メソッドを使うようにします。
コードとしてはこれで良く、実行もできる状態になりましたが、 ACTION_DOWN はまだよいとしても、 ACTION_MOVE は画面から手を離さない限りは定期的に呼び出されてしまいますので、 コード追加位置としては不適切でした。
画面から手を離したときに呼び出される、ACTION_UP の分岐を追加しました。 これで、画面の下のほうで手を離したときに、ブラウザでウェブサイトが開かれることになります。
複数のブラウザがインストールされていて、デフォルト、つまり「常時」に設定していない場合には、ブラウザの選択になります
ブラウザを選択すると、ちゃんと指定のページがブラウザで開かれました。
Canvas だけでもアプリはできる! #1
Android Studio で文字を自由に描画する
アプリ独自のビューを作成し、テキストを描画するまでのコードを作成しています。
Canvas だけでもアプリはできる! #2
Android Studio で画像を自由に描画する
透過ありの PNG 画像をリソースに追加し、等倍で、あるいは拡大・縮小して描画します。 タッチ処理も追加し、タッチした位置に画像が追従してくるコードにしています。
Canvas だけでもアプリはできる! #3
Android Studio でタイマーによる自動画面更新
MainActivity にタイマーを設定して、一定間隔で処理を呼び出すコードを作成しています。
Canvas だけでもアプリはできる! #4
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++ ネイティブアプリでアプリ名を設定する方法の詳細を検討しています。
Android 開発に関する記事をまとめた Android 開発トップ もご覧ください。
以降の数学の基本となる素因数分解の基本部分を、ひたすらトレーニングするための JavaScript コードについて書いています。
因数分解の公式の一部について、ひたすらトレーニングするための JavaScript コードについて書いています。