2014/08/30

[android]ListViewに、文字と値のセットを持たせたい

Androidアプリで、ListViewを表示させようと思った。
と一言書かないといけないくらい、Androidのアプリはわかっていない。

今回表示させたいのは、メニュー画面と思ってもらおう。
タップしたら、別の画面に遷移するという、よくあるやつだ。
AdapterView.OnItemClickListenerをimprementsするなりnewするなりしてonItemClick()を呼んでもらえるようにすると、タップした要素が取ってこれる、というのはわかった。
気になっているのは、そこからどうやって期待する画面に遷移させるか、だ。

タップしたところの文字列が取れるけど、それを比較するのは、なんか嫌だ。
文字列の比較って、最初から最後まで一致することを比較することになると思うので、文字数分のコストがかかってしまう。
それに、何らかの理由で同じ文字列が複数あったら、もうだめだ。

じゃあ、タップしたインデックス値ならどうか。
それだと、値は一意で決まるから、同じ文字列があっても問題はない。
比較するのも1回分のコストで済む。
ただ・・・表示する文字列は<string-array>でやろうと思っているので、書くのはXMLになる。
もし気分が変わってメニューの項目順を変えてしまったら、実装の方も変更しないといかん。
うーん、それは気が進まない・・・。

そこで思ったのが、文字列と値をセットにしたものをListViewに持たせられないか?というもの。
調べてみると、どこかのサイトに、toString()で返す値が表示されるだけだよ、とあった。

    class ListData {
        public String title;
        public int value;
        public ListData(String title, int value) {
            this.title = title;
            this.value = value;
        }
        @Override
        public String toString() {
            return title;
        }
    }

こういうのでよいみたい。

    <string-array name="main_list_label">
        <item>Read</item>
        <item>Write</item>
        <item>Format</item>
    </string-array>
    <integer-array name="main_list_label_num">
        <item>1</item>
        <item>2</item>
        <item>3</item>
    </integer-array>

こんなarrayたちを作って、

        String[] titles = getResources().getStringArray(R.array.main_list_label);
        int[] title_nums = getResources().getIntArray(R.array.main_list_label_num);
        ListData[] list_data = new ListData[labels.length];
        for (int i=0; i<labels.length; i++) {
            list_data[i] = new ListData(labels[i], title_nums[i]);
        }
        ListView lv = (ListView)findViewById(R.id.listView);
        ArrayAdapter<ListData> adapter = new ArrayAdapter<ListData>(this, android.R.layout.simple_expandable_list_item_1, list_data);
        lv.setAdapter(adapter);

こうやってやると、

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        ListView listView = (ListView)parent;
        ListData item = (ListData)listView.getItemAtPosition(position);
        Log.d("TEST", item.title + " : " + item.value);
    }

こんな感じで取得できた。

できれば、順番的な値じゃなくて、直接起動するActivity名みたいなものを持たせたいと思っている。
まあ、そういうのができるか調べんといかんが、Javaはクラス名なんかもバイナリで持ってるし、AndroidManifest.xmlにもActivity名を書くくらいだから、できるんじゃないのか?
Activity名だったら見た目もわかりやすいし、もしソースと名前が一致してなかったらエラーになってくれるだろうし。


私としては満足だったんだけど、Stack Overflowに同じようなことをしたい人がいた。
http://stackoverflow.com/questions/2265661/how-to-use-arrayadaptermyclass

これがいいんじゃないか、という回答が多かったのは、ArrayAdapterをextendsする方法だった。
toString()を追加するタイプは2番目で、しかもコメントで「だめだだめだー」みたいに書かれてあった。

私にとってはtoString()を追加する方がわかりやすい(し、楽だし)のだが、何か理由があるんだろうか?
また、ArrayAdapterをカスタマイズする方が優れていたとしても、toString()の方がダメっていう理由がよくわかっていない。

そもそも、ListViewから画面遷移をするときにこういう方法はとらないのか?
いろいろと疑問が出てきてしまう。。。

へるぷみー。


最近、あれやったり、これやったりに見えてしまうが、実はそうでもない。
Vuforiaにせよ、FeliCa Lite-Sにせよ、動かす環境でアプリを作ることができないと、魅力は半減だ。
一応うちには、Android端末(Nexus7)とiOS端末(iPad mini old)がある。
Vuforiaだったら、どっちもありなのだが、NFCとなるとAndroidになる。

だから、Androidアプリにもう少し力を入れよう、と思い至ったのだ。

2 件のコメント:

  1. ArrayAdapterをextendsするのがListViewの王道的な使い方で、処理の追加、動きの変更もしやすいのでオススメです。
    toStringは基本的にそのクラス、インスタンスを説明する情報を出力するべきなので、あまりプログラム内で使うべきじゃないって感じです。
    捨てコードとか、やっつけ仕事の時はtoStringでもいいかもしれません。

    toStringの参考記事
    Effective Javaの第3章 項目10のtoStringを常にオーバーライドするをirenkaでやってみる件 - じゅんいち☆かとうの技術日誌 http://d.hatena.ne.jp/j5ik2o/20081211/1228962347

    返信削除
    返信
    1. なるほど、Javaとしてのお作法というか、言語仕様の意図があるのですね。
      理由がわからないと、こういう「王道としては、こう」というやり方と「こっちの方が実装的に楽」があると、
      後者を選んでしまうんですよね・・・。
      extendsする方でやってみます!

      削除

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。