シェフのアプリ開発秘話~技術的要素を添えて~


こんにちは。TechBoosterのメンバーのひとり、ふるしんです。

Android Advent Calendar

このエントリは、Android Advent Calendar 2011という企画の裏エントリです。

毎日1人ずつAndroid関係の記事を書いていく、というものです。
他にもたくさん面白い記事があるので、是非読んでみてくださいね。

みんなのクオリティがめちゃくちゃ高くてgkbr…

本題

さて、ここからが本題

私事ですが、11月12日に、Androidアプリ「ディズニー待ち時間」をリリースしました。

午前3時半にリリースしたから、実質11日ってなってるんですよ。
ということで、これからは11月11日は
「ポッキーとプリッツとディズニー待ち時間の日」
ってことにしましょう。えぇ。そうしましょう。

さて。

まず、このアプリを開発する事になった経緯から。

ボクはディズニーが大好きです。
えぇ。2日分6度の飯よりディズニーが好き。

ミッキーさんって稀に呼ばれます。
けど一番好きなキャラクターはドナルドです。
ミニーちゃん可愛いです。

で、だ。

ボクは、ディズニーが好きだ。
ボクは、Androidも好きだ。

そこで、だ。

ディズニー好き && Android好き

そんな人はなかなか居ないでしょう。

Answer:ボクがディズニーのアプリ作っちゃえばいいじゃないですか。

Android Advent Calendarのエントリーのひとつで、@gabu さんの記事が、本当にリアルタイムでぴったりな内容でした。
きっと不便を便利にするためにコードを書き始めた僕たちへ

先日、ディズニーへ友達と一緒に遊びに行った時の会話
ふるしん「正確な待ち時間を表示するアプリ無いの?」
友人「無いよ、超不便だよ、作れる?」
ふるしん「おうよ」

はい。それでできました。ディズニー待ち時間

今日現在では、ついに7万DL超えてます。本当にありがとうございます。

ここでは、ディズニー待ち時間を開発するに使った、けどめんどくさかったコードを幾つか紹介しようと思います。

1.AlertDialogを次々に重ねる

AlertDialogとは、なんか、あのーあれ、ピョコンッって出てくる箱みたいなん。

TechBoosterでも紹介してます。こちらも御覧ください
ダイアログを表示する / Getting Started

で、そんな事できんの?
って思いながらも実験

できなくはないです。
けど何度か同じ動作をすると落ちる。なるほど。

なるほどわからん。

やり方がイマイチわからなくて悩んでたら、@crimsonwoods様が助けて下さいました。
教えてくださったサイトはこちら
[TIPS]アクティビティの見た目をダイアログにする方法

なるほど、ただのActivityをDialogのように表示することができるんですね。
具体的にはイカのようにするようです。

■AndroidManifest.xml

<activity
       android:name=".ShowMap"
       android:theme="@android:style/Theme.Dialog" >
</activity>

このように、マニフェストにActivityを登録する際にandroid:theme=”@android:style/Theme.Dialog”とやってあげるだけで、簡単にActivityをDialogとして表示することが可能のようです。

Dialogとして表示するように作成されたActivityを呼び出すには、普段Activityを呼び出す方法と同様にIntentを使います。

イカのような感じですね。
■src/MainActivity.java

Intent intent = new Intent();
intent.setClassName(
"com.furusin.www.volunteer.disney.disneywaittime",
"com.furusin.www.volunteer.disney.disneywaittime.ShowMap");
startActivity(intent);

IntentについてはTechBoosterの記事をご参考に。
Intentで画面遷移する(明示的Intent)/Getting started

上記コードは、ディズニーの各エリアを表示するために作ったActicity(ShowMap.java)を呼び出す部分です。

本当に、普通にIntentを使ったstartActivityをやってあげるだけ。
アプリ内ではこんな感じで動いてます。

非常に簡単にできました。

2.ExpandableListViewのカスタマイズ

ExpandableListViewとは、なんか、あのーあれ、ただのリストではなく、属性ごとに分けてミョーンと展開ができるリストのことです。

イカの画像のようなやつですね。

TechBoosterでも使い方を紹介していますので、是非御覧ください。
ExpandableListViewクラスで折り畳めるリストを表示する
ExpandableListViewクラスの表示をカスタマイズする

こいつがなかなか厄介。
Mapを使ったらいいらしいんですが、そもそもMapが理解出来てない。
で、色々と調べました。

結局、Mapの1つ目の要素に当たるものをString[]で、2つ目の要素をString[][]で宣言して作っちゃいました。

上記画像のようにカスタマイズしたかったので、独自クラスを作っちゃいました。

重要なクラスはイカのものです。
■src/MyExpandableListAdapter.java

public class MyExpandableListAdapter extends BaseExpandableListAdapter {
	// Sample data set. children[i] contains the children (String[]) for
	// groups[i].
	private LayoutInflater layoutInflater_;
	private String[] parent;
	private String[][][] children;
	private int id = 0;

	Context context;

	public MyExpandableListAdapter(Context cxt, String[] parentID,
			String[][][] childID, int ID) {
		//コンストラクタ
		id = ID;
		parent = parentID;
		children = childID;
		context = cxt;
		layoutInflater_ = (LayoutInflater) context
				.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
	}

	public String getChild(int groupPosition, int childPosition) {
	//子要素を返す
	return children[groupPosition][childPosition][0];
	}

	public long getChildId(int groupPosition, int childPosition) {
	//子要素のIDを返す
		return childPosition;
	}

	public int getChildrenCount(int groupPosition) {
	//子要素の数を返す
		return children[groupPosition].length;
	}

	public LinearLayout getGenericView() {
	//親要素のLayoutを返す
		LinearLayout layout = new LinearLayout(context);
		return layout;
	}
	public View getChildView(int groupPosition, int childPosition,
			boolean isLastChild, View convertView, ViewGroup parent) {
		// convertViewは使い回しされている可能性があるのでnullの時だけ新しく作る
			if (null == convertView) {
				convertView = layoutInflater_.inflate(R.layout.expandchildview,
						null);
			}

			LinearLayout layout = (LinearLayout)convertView.findViewById(R.id.expandChildLayout);
	//子要素のViewを決める処理
		}
		return convertView;

	}

	public Object getGroup(int groupPosition) {
	//リストのグループを返す
		return parent[groupPosition];
	}

	public int getGroupCount() {
	//リストのグループの数を返す
		return parent.length;
	}

	public long getGroupId(int groupPosition) {
	//グループのIDを返す
		return groupPosition;
	}

	public View getGroupView(int groupPosition, boolean isExpanded,
	//グループのViewを返す
			View convertView, ViewGroup parent) {
	//グループのViewを作る処理
		return layout;
	}

	public boolean isChildSelectable(int groupPosition, int childPosition) {
		return true;
	}

	public boolean hasStableIds() {
		return true;
	}
}

ソースコードが長くなっちゃってごめんなさい。
重要なのは、色が変わっている37, 49, 84行目です。

37行目のgetGenericView()でグループのレイアウトの土台を生成し、
72行目のgetGroupView()でグループのレイアウトの中身を作っています。

そして、42行目のgetChildView()でグループの子要素のレイアウトを作っています。

ここがなかなか理解にしくくて、各関数の頭にLog.d()を入れまくりました。
大変でした…

上記コードで作った独自Adapterを、ActivityでExpandableListViewにセットしてあげるだけです。
具体的にはイカのようにします。
■src/MainActivity.java

MyExpandableListAdapter mAdapter = new MyExpandableListAdapter(
					context, area_name, attrs, 0);
ExpandableListView lv = (ExpandableListView) findViewById(R.id.expandableListView1);
lv.setAdapter(mAdapter);

これだけ。本当に普通にセットするだけ。

苦労した場所はもっともっとたくさんありますが、書き切れないのでここらへんで止めておきましょう。

以上、「シェフのアプリ開発秘話~技術的要素を添えて~」でした。

最後に

このアプリのアイコンをデザインしてくださった@xxxmako さん、本当に可愛いアイコンを作っていただきありがとうございました!
本当に可愛いんですよねこれ…

もうこのデザインとなら心中してもいいくらい。しないけど。

また、Android Advent Calendarで記事を書く、という非常に貴重な経験をくださった@youten_redo さん、ありがとうございました。

皆さま、これからも「変態多きAndroider達」を生温かい目で見守ってあげてください。