[Android]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)
こうすることで、
- 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;
}