技術顧問の増田です。第1,2回と Visual Studio で Android アプリを作る準備ができたところで、いよいよ本格的に Android アプリを作っていきます。「本格的に」とはいえ、販売できるような売り物のアプリを作るのではなくて、社内や個人で使うような「カジュアルなアプリ」を作るところを目標にしていますので、そこのところはお手柔らかに。
バックナンバー
- 第1回 C#で自作Android アプリを作ろう:出帆準備編
- 第2回 C#で自作Android アプリを作ろう:試験運用編
- 第3回 C#で自作Android アプリを作ろう:時計アプリを作る
- 第4回 C#で自作Android アプリを作ろう:RSSを取得してリスト表示
- 第5回 C#で自作Android アプリを作ろう:Web APIで路線情報を表示
- 第6回 C#で自作Android アプリを作ろう:Twitter APIでファボリストを取得
- 第7回 C#で自作Android アプリを作ろう:簡易アンケートを作ろう
- 第8回 C#で自作Android アプリを作ろう:カメラ機能を使おう
カジュアルなアプリの目標
「カジュアルな」と銘打ってみますが、実のところは「勝手アプリ」をさっくりと作ろうというのが主旨です。直接的なマネタイズという視点では、Google Play に公開をしたり、収入が得られる位の機能を盛り込むのがベストなのでしょうが、もうちょっと変化球的に「自分で使うようなツール/アプリをさっくりと作ろう」という目標にしておきましょう。
自分で使うようなツールなので、
- 複雑な設定画面は省略して、コードで直接記述したり、
- 多機能なアプリよりも単機能で済ませるアプリを求めたり、
- 大勢の好みよりも、自分の好みを優先させたり、
します。まあ、そのあたりが「勝手アプリ」の醍醐味だったりするので、高機能な製品アプリとは異なるアプローチで Android アプリを作っていきましょう。
時計アプリを改良する
前回サンプルとして作成した時計アプリを、少し改良していきます。
こんな感じで、時刻表示がチープだったものを、
こんな風に、時刻を表示する場所やフォントの大きさを変えていきます。
画像の切り替えや、日時表示をするときのフォントの大きさとかを色々カスタマイズできるのもよいのですが、ここではシンプルにコードで実装してしまいましょう。
- ホームで表示されるアイコンの変更
- 現在時刻が表示される場所を変更
- 現在時刻と現在日付が切り替わるようにする
- 画像が定期的に切り替わるようにする
これらの機能を順番に実装して、簡単に解説します。
サンプルコード
サンプルコードは sg-xamarin-sample/src/sgClock にあるので、ダウンロードして参考にしてください。
ここでの完成品は sgClock2 になります。
アイコンの変更
最初にホームに表示されるアイコンを変更してみましょう。Visual Studio で Android アプリを作ると、標準の Android のアイコンになっているので、これを変更します。
一番簡単なのは、Icon.png を上書きしてしまうことですね。sgClock2 プロジェクトも上書きしてしまっています。
実は、このアイコンの指定は、MainActive.cs に記述されています。
1 2 3 4 |
[Activity(Label = "sgClock2", MainLauncher = true, Icon = "@drawable/icon")] public class MainActivity : Activity { ... |
Activity 属性の Icon プロパティにアイコンのリソースを指定するので、これを切り替えることでもホームのアイコンを指定できます。ここの属性ってのは鍵括弧/大括弧([])で囲まれているところで、クラスやメソッドなどの静的に指定できるものです。Java で指定するアノテーションと似たようなものです。
この属性を上手く使うと、外部設定が減るのですが、それはまた別の機会に。
こうすると、他のアプリ区別がつきますよね。実験をしているときは、ドロイド君そのままでもいいんですが、区別がつかなくなってしまうので。ちなみに、社内ツールなんかは、いちいち変えるのも面倒なので、同じアイコンにしてしまうのもありです。そうそう、所詮、個人ツールですから版権ものもアリですね。
現在時刻の位置を変える
次は、現在時刻を表示している位置をかえてみましょう。
これだと、センタリングはしてあるけど、装飾性に欠ける…というか、まるっきり面白くないですよね。
スマートフォンの画面レイアウトは結構面倒くさくて、Android 機種ごとに解像度が違ったり、縦置きしたり横置きになったりして色々対応しようとするとかなり面倒です。ちまちま1ピクセル単位で位置合わせをするとか(最終的に綺麗なレイアウトを作るときは必要なんですが)、使いやすいインターフェースを考えるときにあれこれと試行錯誤をします。
で、この「面倒臭さ」を回避するコツがあります。回避するだけなので、綺麗な UI のアプリを作るときには別な手段を使うのですが、社内ツールのようにほどほどのレイアウトで済む場合には、ある手順で作っていったほうが開発時間が少なくて済みます。
- チープな画面だけど、部品は揃っている状態を作る。
- 動作する部品の名前を UI とコードで結び付ける
- レイアウトを大幅に好みに変更する。
この3つのステップで作ります。
①の「チープな画面」ってのは、sgClock のような画面です。タイマーのタスクを使って、一応動くような作りになっています。面白味はないですが、この状態で機能は揃っているといえます。
②の名前を合わせるってのは、こんな風に axml 上では「@+id/textView1」になっていて、C# のコードからは「text1」で参照する、というのな形です。
ここでは、名前を変えてしまっていますが、できることならば「text1」とかに統一してしまとよいでしょう。これを結び付けているのが、
1 |
text1 = FindViewById(Resource.Id.textView1); |
のような FindViewById メソッドを呼び出すコードです。FindViewById メソッドを使えば、リソースから取ってこれるので、これをあちこちで使ってもいいのですが、OnCreate で一括して取得するようにします。そして、内部の text1 プロパティで TextView を参照するように統一しておきます。
これ自体は、Windows プログラミングの XAML を使ったアプリでは自然なのですが(というか、自動でフィールド化される)、Xamarin.Android で作る場合にもこれに合わせておきます。この先に、MVVM パターンがあるわけですが、そこまで至らなくて時計ツールは作れるのでここでは止めておきましょう。必要になったときに覚えるというスタイルにします。
これで、UI とコードが厳密に結び付いたので、③でレイアウトを大幅に変更していきます。
どれくら大幅に変更するかというと、元のレイアウトが LinearLayout で作られているものを、RelativeLayout に切り替えてしまいます。
- LinearLayout は自動的にコントロールを積み重ねて配置する
- RelativeLayout は位置を指定してコントロールを配置する
このような違いがあるので、時刻の位置を自由に変更したいときには RelativeLayout を使うと便利です。
1 |
RelativeLayout タグを配置させて、layout_marginTop や layout_marginTop を使って調節していきます。 このあたりは、Designer を使って微調節をしてもよいし、Source で axml ファイルを直接変更してもよいでしょう。デザイナで大まかな位置を合わせた後で、Source を開いて位置の数値を揃えていくとうまくいきます。
うまく調節すると、こんな風に画像の上に TextView を重ねられるようになります。
ここまでダイナミックにレイアウトを変更しても、①と②が変わらない限り、プログラムは正常に動きます。UI の試行錯誤と動作するコードの試行錯誤を分離させるのが、効率よく開発するためのコツになります。
現在時刻と日付の切り替え
時刻だけの表示じゃなくて、日付を表示するようにしてみましょう。常に表示するのではなくて、画像部分をタップしたときに、時刻から日付へ、逆に日付から時刻へ表示が変わるようにします。
画像をタップしたときには Click イベントが動くのですが、以下のように image1 の Click にイベントを結び付けます。
1 2 |
image1 = FindViewById(Resource.Id.imageView1); image1.Click += Image1_Click; |
Visual Studio で作ると「image1.Click += 」と打ったときに、自動的に Image1_Click メソッドを作ってくれるので、これをそのまま使います。
タップしたときに、変数 timeFormat の値を true/false で切り替えます。
1 2 3 4 5 6 7 8 9 10 11 |
private void Image1_Click(object sender, EventArgs e) { if (timeFormat == true) { timeFormat = false; } else { timeFormat = true; } } |
ここは、冗長に if 文を使っていますが。「timeFormat = !timeFormat」な書き方でも ok です。まあ、自分が分かりやすい書き方にしておいてください。
でもって、TextView コントロールに時刻表示をしていた部分を以下のように書き替えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
_timer = new Task(async () => { while (true) { RunOnUiThread(() => { if (timeFormat == true) { text1.Text = DateTime.Now.ToString("HH:mm:ss"); } else { text1.Text = DateTime.Now.ToString("yyyy-MM-dd"); } }); await Task.Delay(1000); } }); _timer.Start(); |
実は、これだとタップした瞬間に変わるのではなくて、1秒ごとに時刻の表示を変えるタイミングで切り替わることになるのですが、これで良しとしましょう。
気になる人は、タップした瞬間に時刻と日付が切り替わるように修正してみてください。Image1_Click メソッド内で1度だけ更新してあげるとうまくいきます。
画像を10秒ごとに入れ替える
最後に画像の切り替えに挑戦してみましょう。sgClock2 のサンプルで使っているのは弊社のロゴの色違いなのですが、皆さまは好きな画像を使ってみてください。まあ、適当な版権ものを貼り付けて試すのが手っ取り早いですよね。というか、そういう目的の画像の切り替えのサンプルですから、是非。
切り替えるための画像ファイルは、Icon.png と同じように Resources/drawable フォルダに PNG 形式置くとコードが楽になります。リソース内の画像を切り替えるだけですからね。先行きは、ネット上の画像を自動で取ってきたり、Web API を使って画像ファイルのリストを取得して切り替えたりするものを作るのもよいのですが、まずは、リソースにある固定ファイルにしておきます。
1 2 3 4 5 6 7 8 |
int[] bmps = new int[] { Resource.Drawable.sg400_1, Resource.Drawable.sg400_2, Resource.Drawable.sg400_3, Resource.Drawable.sg400_4, Resource.Drawable.sg400_5, }; int index = -1; |
1 |
画像リソース番号を持っている配列を用意しておいて、定期的に切り替えるだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
_timer = new Task(async () => { while (true) { RunOnUiThread(() => { if (timeFormat == true) { text1.Text = DateTime.Now.ToString("HH:mm:ss"); } else { text1.Text = DateTime.Now.ToString("yyyy-MM-dd"); } // 画像を切り替える if (DateTime.Now.Second % 10 == 0) // ① { index++; if (index >= bmps.Length) index = 0; image1.SetImageResource(bmps[index]); // ② } }); await Task.Delay(1000); } }); _timer.Start(); |
- 10秒おきに表示が切り替えられるように、10で割ったあまりが0になったときにindexを繰り上げます。
- リソースから ImageView に表示するときは、SetImageResource メソッドを使います。
できあがり
完成したコードはこんな感じになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
[Activity(Label = "sgClock2", MainLauncher = true, Icon = "@drawable/icon")] public class MainActivity : Activity { TextView text1; ImageView image1; Task _timer; bool timeFormat = true; protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); // Set our view from the "main" layout resource SetContentView(Resource.Layout.Main); // Get our button from the layout resource, // and attach an event to it text1 = FindViewById(Resource.Id.textView1); image1 = FindViewById(Resource.Id.imageView1); image1.Click += Image1_Click; int[] bmps = new int[] { Resource.Drawable.sg400_1, Resource.Drawable.sg400_2, Resource.Drawable.sg400_3, Resource.Drawable.sg400_4, Resource.Drawable.sg400_5, }; int index = -1; _timer = new Task(async () => { while (true) { RunOnUiThread(() => { if (timeFormat == true) { text1.Text = DateTime.Now.ToString("HH:mm:ss"); } else { text1.Text = DateTime.Now.ToString("yyyy-MM-dd"); } // 画像を切り替える if (DateTime.Now.Second % 10 == 0) { index++; if (index >= bmps.Length) index = 0; image1.SetImageResource(bmps[index]); } }); await Task.Delay(1000); } }); _timer.Start(); } private void Image1_Click(object sender, EventArgs e) { if (timeFormat == true) { timeFormat = false; } else { timeFormat = true; } } } |
実行した結果はこんな風になります。
実はアナログ時計も作れる
実は、Android にはアナログ時計を作る AnalogClock というコントロールがあってですね。
1 |
こんな風にあっさり作れたりします。
まとめ
如何だったでしょうか。これで Android で時計アプリを作れそうですよね(アナログ時計であれば一瞬でできます)。
作り方のコツとして、
- チープな画面で動作を確認する
- UI とコードを結び付けて、名前を決めておく。
- UI をダイナミックに変更する
の順番を守っていけば、手早く Android アプリが作れるようになります。
次回は、RSS を読み込んでリストに表示する、アプリを作っていきます。お楽しみに