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;
    }