ExpandableListViewとSimpleExpandableListAdapterの構造
ExpandableListViewはタップすると子要素を開く、ListViewの一種です。これを使うことで、一段階までの要素を表現できます。
ListViewと同じくこれもAdapterを使って表示内容のデータを保存します。Adapterにはいくつかあるのですが、一番手軽に使えそうなSimpleExpandableListAdapterで、意外にはまってしまったのでここに記します。
階層構造では、親と子の対応関係が必要なわけですが、SimpleExpandableListAdapterでは、親と子の対応関係を明示的に設定することはしません。親リストと子リストの中で何番目なのかということで、親子関係を設定します。
つまり、こうなります。
- 親ノードリスト (Mapのリスト)
- [0]: 1番目のグループ (A)
- [1]: 2番目のグループ (B)
- [2]: 3番目のグループ (C)
- 子ノードリスト (Mapのリストのリスト)
- [0]: 1番目のグループに入れるリスト (Mapのリスト)
- [0]: 1番目のグループに入れる1つ目のデータ(Map) (a)
- [1]: 1番目のグループに入れる2つ目のデータ (b)
- [2]: 1番目のグループに入れる3つ目のデータ (c)
- [1]: 2番目のグループに入れるリスト
- [0]: 2番目のグループに入れる1つ目のデータ (d)
- [2]: 3番目のグループに入れるリスト
- [0]: 3番目のグループに入れる1つ目のデータ (e)
- [1]: 3番目のグループに入れる2つ目のデータ (f)
- [0]: 1番目のグループに入れるリスト (Mapのリスト)
こうすることで、
- A
- a
- b
- c
- B
- d
- C
- f
という親子関係を持ったExpandableListViewを実現できます。分かりにくいですかね…
サンプルコード
以下にサンプルコードを示します。このコードでは、IDと日付を持っているHogeDataクラスのリストを日付ごとに整理して表示します。日付を親とし、その日付を持っているHogeDataクラスを子として表示するわけです。
なお、元のコードからコピペと改変をしたので、実際に動くかどうかは確認していません。インデントも崩れてしまっています。心を読んでください ;-)
ちなみにライセンスは参考にしたGoogleのサンプルコードと同じくApache Licenseとさせてください。
public void setExpandableListView(ArrayList<HogeData> hogeList){ ExpandableListView listView = new ExpandableListView(getApplicationContext()); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); List<Map<String, String>> groupData = new ArrayList<Map<String,String>>(); // 親ノードリスト List<List<Map<String, String>>> childData = new ArrayList<List<Map<String,String>>>(); // 子ノードリスト for(Iterator<HogeData> iter = hogeList.iterator(); iter.hasNext();){ HogeData ho = iter.next(); List<Map<String, String>> children; // 子要素を宣言 String date = format.format(ho.getDate()); int location = getParentGroup(date, groupData); // 親リストの場所を取得 if (location == -1){ Map<String, String>groupMap = new HashMap<String, String>(); groupMap.put("date", date); groupData.add(groupMap); // 親リストに追加 children = new ArrayList<Map<String, String>>(); // 対応する子要素を作り、追加 childData.add(children); location = 0; }else{ children = childData.get(location); } // データを作成・追加 Map<String, String> curChildMap = new HashMap<String, String>(); curChildMap.put("id", ho.getId()); curChildMap.put("date", ho.getDate()); children.add(curChildMap); } // アダプタ設定 ExpandableListAdapter mAdapter = new SimpleExpandableListAdapter( getApplication(), groupData, android.R.layout.simple_expandable_list_item_1, new String[] {"date"}, // 親のMapで表示するデータを設定 new int[] { android.R.id.text1}, childData, android.R.layout.simple_expandable_list_item_2, new String[] { "id", "date"}, // 子の表示データ new int[] { android.R.id.text1, android.R.id.text2} ); listView.setAdapter(mAdapter); // クリックした時の動作を設定 listView.setOnChildClickListener(new ExpandableListView.OnChildClickListener(){ @SuppressWarnings("unchecked") // チェックなしキャストの警告を抑制 public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { ExpandableListAdapter adapter = parent.getExpandableListAdapter(); Map<String, String> childMap = (Map<String, String>)adapter.getChild( groupPosition, childPosition ); Log.d("ExtView", "id:" + childMap.get("id") + "/date:" + childMap.get("date")); } }); layout.addView(listView); return layout; } /** * 引数keyをkeyとして持っているMapが * 引数listのリスト内にあるかどうか調べ、あった場合にはその番号を、ない場合は-1を返す。 * @param key 調べるkey * @param list 調べるリスト * @return list内の場所 */ public int getParentGroup(String key, List<Map<String, String>> list){ int location = 0; for(Iterator<Map<String, String>> iter = list.iterator(); iter.hasNext();){ Map<String, String> map = iter.next(); if (map.containsValue(key)){ return location; } location++; } return -1; }