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


こんにちは。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

1
2
3
4
<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

1
2
3
4
5
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

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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

1
2
3
4
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達」を生温かい目で見守ってあげてください。