Android プログラミング(3) – Activity 入門
シリーズとして、下記の書籍にて学習したことについてのメモを綴っています。
今回は、「HACK#6 アクティビティ入門」 (p.41~58) の内容について触れていきます。
いよいよプログラミングです! 張り切っていってみましょう。
アクティビティって何だ?
アクティビティとは、画面を持った機能の単位を指します。 Android では、アクティビティ毎に状態を管理する仕組みがあり、特殊な場合を除いて画面の遷移もアクティビティ単位で行います。
なるほど。モバイル機器向けの OS と割り切ってか、アプリが画面を占有することが前提の世界観になっているわけですね。 Windows でのアプリ開発がウィンドウ主体で行われたり、 Web アプリのクライアントスクリプト部分の開発が DOM 主体で行われたりするのとはまた対照的です。
また、この Hack では本書のサンプルコードが利用できます。サンプルコードは
trunk/Hack6/HelloActivity
にあります。
こんな事が書いてありますが、この本、別に CD とかは付いてきません。いきなりこんなこと書かれちゃうと何の事やらさっぱりで混乱しちゃう読者とか結構いそうな気がするんですが… (^_^;)
で、その問題のサンプルコードですが、こちらのサイトから入手可能なようです。 Subversion で checkout できるのはいいですね。 git でコピーして改造コードを独自に管理したりするとなお幸せかもしれません。
…ですが、私はここは敢えてこのサンプルコードには手を出さず、本書に書かれたソースコードを手で入力しながら学習してみることにしました。写経は学習の基本です…多分。
さて、それでは本書に習って Eclipse 上で新規プロジェクトを作ってみましょう… おっと、そうだった。新規プロジェクトは、すでに開発環境の設定の際にお試しで作っちゃっていました。今回はこいつをそのまま利用することにします。
アプリケーションを実行するには図 6-3 のようにプロジェクトツリーでプロジェクトのルートを選択した状態で、再生ボタンの形をしたアイコンをクリックします (Run メニューの Run を選択しても可)。
これ、プロジェクトツリー上での選択は必ずしも必須ではないみたいです…。もしかしたら複数のプロジェクトが存在するようなケースではここでの選択が意味のあるものになるのかもしれませんが、とりあえず何も選択しないままツールバーの再生アイコンをクリックしても全く問題なく起動します。
むしろ、ここでエミュレータが初めて起動する際には、起動直後はエミュレータがロック状態になっていて、そのままではアプリが実行されないので、エミュレータ側でロックを解除してあげる必要があります。ロックを解除してしばらくすると、アプリが実行されるようです。
何も考えずにプロジェクト名を「TestSomething」として作ると、こんな内容の実行画面になります。 Activity の名前はプロジェクト作成時に指定可能…。
static な文字列管理は strings.xml で
ところでこの画面に表示されている文字列、いったいどこに書かれているんでしょう? メインアクティビティクラス (私の場合、今回は TestSomethingActivity.java
) のソース中にはこんな文字列は存在しませんでした。
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); }
setContentView
メソッドで指定しているR.layout.main
は、自動的に生成される XML の画面レイアウトリソースです。
なるほど。それでは main.xml
の内容を覗いてみましょう…。
<TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" />
android:text
という属性名があからさまにアヤシイですね! @
で始まるのは何かの識別子でしょうか? なんとなく、これは別のリソースを参照しているのではないかという気がします。
@string
で始まっているので、 string.xml
なんてファイルでもあるのかな? と Package Explorer の中を探ってみると… res/values
の位置に、まさにそのものずばりのファイル strings.xml
を見つけました!
反転部分がまさに表示されていた文字列そのものですね。どうやら当たりのようです。早速書き換えて実行してみましょう!
ここははらぺこらしく…w
ちなみに画面の雰囲気が大分変わってますが、実はここを弄っていた当初、スクリーンショットを撮るのに失敗していたようで、その後、さんざん弄った (Eclipse のフォント設定含めて^_^;) 後になって慌てて撮り直したためです… m(_ _)m
実行すると、以下のように表示されました。日本語も化けていませんね。大成功です v(^-^) 。
こうしてみると、なるほどどうやらアプリで使用する static な文字列は、この
strings.xml
ファイル内にリソースとして記述し、それを参照するように実装するのが良さそうです。決めうち定数をどのようにして一括管理するべきかというのは、開発においては結構重要なポイントですよ。
アクティビティのライフサイクル
p.45~48 に渡って、アクティビティの生成、起動、中断、破棄などのライフサイクルと、それに応じて呼ばれるメッセージハンドラメソッドの説明、それらが呼ばれるタイミングを示した図が示されています。
「図6-12 画面が回転した時」とあって、ここの意味がよく分からなかったので調べてみました。実は私は Android 端末はまだ入手していないのでピンと来なかったのですが… (社費で中古の白ロムでも買おうかなぁ…)、どうやら画面を縦横に傾けるとアプリの画面もそれに応じて向きが変わるようになっていて、そのことを言っているようです。画面回転時の再描画を実現するために、一旦アクティビティを破棄して生成し直す、ということをやっていて、その辺の一連のメッセージが全部飛んでくるから適切に対処しなきゃいけない、という話なのでした。
アクティビティの遷移
画面遷移をするために他のアクティビティを起動させます。まずは起動するアクティビティを用意するために、
MainActivity.java
をコピーしてSubActivity.java
を作成します。
これ、もうちょっと上手い方法は無いのでしょうか…? いろいろと実装してコード量が大きくなってしまったファイルをコピーして、不要なコードを削除して書き直す、というのはちょっと面倒です。かといって、単純にクラスを新規作成だと、 extends
と、それに必要な import
行ぐらいは出力してくれるものの、それ以外のよく使うライブラリの import
行や onCreate()
メソッドなどの冗長なコードを手作業で書き加える必要があるし、この後にまたコピペして作っているレイアウトリソースの XML ファイルも手作業で作らなきゃなりません。 XML ファイルのみを作成するウィザードは一応あるようですが、どうせならアクティビティ作成用のウィザードも ADT にて用意しておいていただきたいものです…。
10: android:text="Sub activity"
もちろんここは、任意のテキストを指定できます。日本語でも問題ないようです。こんな感じで…w
android:text="飯でも食うか ((( ´ω`)=3"
main.xml
を以下のように修正します。 (ボタンの使い方は「Hack #9 ボタンを使う」で詳しく紹介しています。ここではおまじないだと思ってそのまま入力してください。)
私自身がそうなのですが、未知の技術を習得しようとする人が、講義の中で本論ではない未知の記述が出てきた時によく使われる「おまじない」という言葉に戸惑ってしまう、ということは少なくないようです。よく分からないものをよく分からないまま、ブラックボックスのままの状態で後回しにされるのが苦手なんですね。そんなわけで、 HACK#9 の該当部分を先回りして読んでみました。内容を要約すると概ね以下の通りです。
android:id
属性にボタン要素の ID を指定する。
のように書くことによって、@+id/
(ID 名)Java
コード中から
として参照することが出来るようになる。R.id.
(ID 名)android:layout_width
属性にwrap_content
と指定すると、ボタンの表示幅かボタン内のラベル文字列にフィットするようになる。android:text
属性にボタンのラベル文字列を設定する。
ここでは特に ID の指定と利用の方法が重要ですね。この予備知識を元に、その後に出てくる Java のサンプルコードを読んでみると、より理解がスムーズに行くのではないかと思います。
5~8行目の
View
、Button
、OnClickListener
、Intent
クラスを使うために、import
文を追加しています。
import
行の行数がやたらと多くなってしまう辺りは如何にも Java という感じですね。クラス内クラスはグロブ ((パッケージ)
みたいな書き方) じゃ拾えないですしね。ていうか、クラス内クラスはむしろ名前空間に展開しないでコード中で .*
みたいに書かせちゃった方が、むしろセマンティクス的にも閲覧性は上がるような気もします…。new View.OnClickListener()
DDMS って何? LogCat って何?
さらにアクティビティを追加したことを
AndroidManifest.xml
に追記してやる必要があります。追記せずにSubActivity
を起動させるとエラーが出てしまいます (図 6-16)。
ほぅほぅ、それは是非とも試しておかなきゃですね! というわけで、AndroidManifest.xml
を弄る前にとりあえず実行して、ボタンを押すとエラー画面がでてアプリが終了してしまうのを確認しました。
で、ここまではよかったのですが、その直後。
DDMS のパースペクティブで LogCat のログを確認すると以下のようなエラーが赤い文字で出ているのがわかります。
さっぱりわかりません。 DDMS って何? LogCat ってどこで見れるの??
Web の時代に生きていることをこれほど感謝することはありませんね…。ぐぐればその答えはすぐに見つけることが出来ました。
DDMS は Dalvik Debug Monitor Server の略で、 Android には標準で装備されているものらしいです。 ADT プラグインを入れた Eclipse 上では Android SDK アプリのデバッグの際に Android 端末やエミュレータ上の DDMS と通信してデバッグを実現しています。上記の引用では恐らくデバッガそのもののことを DDMS と呼んでいらっさるのでしょう。
デバッガの話なのでデバッグ実行してあげる必要があるのですが、 Eclipse 上でツールバーの虫アイコンをクリックしてデバッグを開始しても、 LogCat とやらは表示されません。デバッグの状況をモニタするには、パースペクティブも Debug のものに切り替えてあげる必要があります。 Eclipse の画面右上の所にある「Debug」ボタンをクリックするか、または [Window] → [Open Perspective] → [Debug] メニューを選択すると、 Debug のパースペクティブに切り替えることができ、めでたく LogCat を拝むことができるようになります。
しかしこの画面ではログの全文に目を通すことができないので、コピペしてテキストエディタにでも貼りつけましょう。 LogCat にて (これ自体はログを一覧するマルチカラムのリストコントロールになっています)、コピーしたいログを選んで (複数選択できます) [Ctrl + C] キーを押すと、選んだログの全文がクリップボードにコピーされます。なんて直感的! w
これで、DDMS がはき出したエラーログの全文に目を通すことができるようになりました。うん。確かに、本書に書かれている通りのメッセージが出力されているようですね。
マニフェストの追記
クラスだけを追加して
AndroidManifest.xml
への追記を忘れることはよくある (筆者だけ?) ので、
いえいえ、多分私もしょっちゅう忘れそうです。しかしリソース XML 然り、クラスの冗長な部分然り、アクティビティの作成に際してはあまりにも多くの部分が冗長な手作業になっていて悩ましいところですね。なんかその辺自動化できるようなプラグインって無いのかなぁ… 自作するか?
状態の保存、復元
本書ではここからサンプルコードが変わるのですが、やってる内容的に言ってもこれだけのためにまた一からプロジェクト作り直すのも面倒だったので、これまで使ってきたプロジェクトに、そのままここからの内容を付け足していくことにしてしまいました。
で、思ったのですが、恐らくサンプルコードでは最初から用意してあるものの、本書では触れられていないためにひっそりとおまじない扱いにされている要素がいくつかあることに気づきました。やっぱり学習の基本は写経ですね! というわけでその辺について触れていきましょう。
追加すべき import 行
このサンプルプログラムではボタンの他にイメージビューと呼ばれるコントロールを利用しています。その為、以下の import
行を追加してあげる必要があります。
import android.widget.ImageView;
とはいえ、ボタンも同時に利用しているのですから、このように書くと、実際には以下のような記述になってしまうでしょう。
import android.widget.Button; import android.widget.ImageView;
実際の開発ではこの 2つに限らず、 android.widget
パッケージに含まれるたくさんのコントロールを同時に使うことになるのではないでしょうか。といったことを考えても、この 2行を、グロブを用いて以下のように 1行にまとめて書くように習慣づけた方が何かと捗るような気がします。
import android.widget.*;
それからもう一つ、このサンプルプログラムでは、乱数を生成するオブジェクトのクラス Random
も利用していますので、以下の 1行も加えてあげる必要があります。これもまぁ、このパッケージの配下のクラスはいろいろと利用頻度高いんじゃないの? という配慮の元、グロブで書いちゃってますが、ここではふつーに完全修飾クラス名で書いちゃってもいいのかなとは思います。
import java.util.*;
画像リソースを用意する
05: int[] images = new int[]{ 06: R.drawable.android, R.drawable.stamp 07: , R.drawable.stamp2, R.drawable.mail};
Java なんだからカンマの手前で改行とかするなよみたいな野暮なことを言うつもりはないのですがw、この
で始まる識別子、当然最初から用意されているわけもなく、この識別子によって参照されるリソースを自分で用意してあげる必要があります。R.drawable.
~
そのリソースとはつまり画像のことです。適当な画像ファイルを 4つばっかし作成し、それぞれ然るべき名前で特定の場所に保存しておいてあげる必要があります。その手順を以下に示します。
- 適当な画像を用意します。画像の形式は JPEG か PNG 辺りが確実でしょう。
- 画像のファイル名を、先ほどの識別子
のR.drawable.
~~
の部分と同じになるように変更します。例えば、R.drawable.android
に対応するファイル名はandroid.jpg
とかandroid.png
とかになります。 - 画像ファイルを、
(プロジェクトルート)/res/drawable-hdpi
ディレクトリ下にコピーします。 - Eclipse 上で [F5] キーを押し、 Package Explorer の表示を更新します。このとき、自動生成される
R.java
も更新され、先ほどの識別子が有効になります。
変数名を変えて入力してみよう
正直なところ、mButton
とか mImageView
とかみたいなハンガリアン記法的な名前規則がなんだか馬鹿みたいで私の性に合わないというのもあったので、メンバフィールドなどの変数名はもうちょっと意味のある名前に変えて入力していました。こうすることで、テキストに書かれているコードをただ書き写すのではなく、どの部分にどの変数を使うべきなのかと言うことをきっちり考えながら入力していくことになるので、理解も早くなるのではないかと思います。
プロジェクトを作り直さずに機能を付け足すような形で実装したため、こんな画面になってしまいましたw ちなみに、 [Ctrl + F12] キーで画面を横向きにすると、画像は画面の大きさに合わせて自動でリサイズされるようです (この辺の動作は
AndroidManifest.xml
で変更できるのかな?)。
まとめ
まだ初歩の初歩だというのにやたらと内容がゴージャスになってしまいました… orz この辺は慣れてきた中盤以降の方がメモも簡潔になっていきそうな予感もありますね。
ここまで文句ばかり書いていましたがw、実際にはかなり丁寧に説明が書かれていて、順を追って読みながら試していけば、すんなりと理解できるようになっているのではないかと思います。今後もこんな感じで学習を進めていきたいと思います。
しかし新規アクティビティ作成用のプラグイン欲しいなぁ… あれだけは本当にいただけないよなぁ… 後でまた少し探してみよう…。
今回は以上です。
2011 年 9 月 15 日 by 村山 俊之
2011 年 9 月 25 日 6:13 PM
[...] このトピックは致命的なことに、アクティビティが有効になる/無効になると言うことがどういう事なのかがいまいちわかりづらくなっています。現象から言えることは、マニフェストに登録したアクティビティが <intent-filter> 要素を伴ってランチャーにアイコンを表示させるようにしていた場合、アクティビティが有効ならばそのアイコンが表示され、無効ならば表示されなくなる、ということだけでした。 もう一つのアクティビティが有効なときのランチャー画面 そこで、メインの方のアクティビティにボタンをもう一つ加えて、そのボタンを押すとサブの方のアクティビティに遷移するようにしてみました。よーするに、前回の前半でやった内容を組み合わせちゃったわけですね。 参考までに、実際に実装したメインの方のアクティビティクラスの onCreate() ハンドラメソッドを以下に掲載します。 [...]