Androidで折りたたみ式リストビューを使うメモ【ExpandableListView】

Androidで折りたたみ式リストビュー(ExpandableListView)を使う方法をメモします。

#ExpandableListViewを持ったレイアウト
##レイアウトの設定
まず、ExpandalbeListViewを持ったレイアウトを作成します。
レイアウトに ExpandableListView をドロップし下記のようにします。

```html:activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

    <ExpandableListView
        android:id="@+id/expandableListView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/textView1"
        android:layout_below="@+id/textView1"
        android:layout_marginTop="14dp" >
    </ExpandableListView>

</RelativeLayout>
```

#ExpandableListViewを使用したActivity
次に、Activityを作成します。
ExpandableListViewで情報を表示するには、SimpleExpandableListAdapterを使います。
SimpleExpandableListAdapterを使用することで、簡単にExpandableListViewへデータを渡すことができます。
リストとして表示したい情報をSimpleExpandableListAdapterへ格納するため、下記の順にMapを作成します。

1. グループの親項目用のリストを作る
1. リスト項目として表示したい子要素全体用のリストを作る
1. グループの親項目用のMapに情報を格納する
1. 各グループ別のリスト項目用のリストを作る
1. 各グループ別のリスト項目用のMapに情報を格納する
1. 子要素全体用のリストに、各グループ別のリスト項目用リストを格納する
1. グループの親項目用のリストと、子要素全体用のリストをSimpleExpandableListAdapterへ格納する

では、実際にコードにします。

##各データの格納
まず、グループの親項目用のMapに、表示したい内容を格納します。

```java:MainActivity.java
int PARENT_DATA = 3;
// グループの親項目用のリスト
List<Map<String, String>> parentList = new ArrayList<Map<String, String>>();
// 親リストに表示する内容を生成
for (int i = 0; i < PARENT_DATA; i++) {
    Map<String, String> parentData = new HashMap<String, String>();
    parentData.put("title", "title" + Integer.toString(i));
    // グループの親項目用のリストに内容を格納
    parentList.add(parentData);
}
```

次に、子要素となる内容を格納します。

```java:MainActivity.java
int CHILD_DATA = 3;
// 子要素全体用のリスト
List<List<Map<String, String>>> allChildList = new ArrayList<List<Map<String, String>>>();

// 子要素として表示する文字を生成
for (int i = 0; i < PARENT_DATA; i++) {
	// 各グループ別のリスト項目用のリスト
	List<Map<String, String>> childList = new ArrayList<Map<String, String>>();

	// リスト項目用データ格納
	for (int j = 0; j < CHILD_DATA; j++) {
		Map<String, String> childData = new HashMap<String, String>();
		childData.put("TITLE", "子要素" + Integer.toString(j));
		childData.put("SUMMARY", "概要" + Integer.toString(j));
		// リストに文字を格納
		childList.add(childData);
	}
	// 子要素全体用のリストに各グループごとデータを格納
	allChildList.add(childList);
}
```

##アダプタに格納
そして、作成した情報をアダプターに格納します。

```java:MainActivity.java
// アダプタを作る
SimpleExpandableListAdapter adapter =
		new SimpleExpandableListAdapter(
				this,
				parentList,
				android.R.layout.simple_expandable_list_item_1,
				new String[] { KEY1 },
				new int[] { android.R.id.text1, android.R.id.text2 },
				allChildList,
				android.R.layout.simple_expandable_list_item_2,
				new String[] { KEY1, KEY2 },
				new int[] { android.R.id.text1, android.R.id.text2 }
);
//生成した情報をセット
ExpandableListView lv = (ExpandableListView)findViewById(R.id.expandableListView1);
lv.setAdapter(adapter);
```

なお、SimpleExpandableListAdapterクラスのコンストラクタは下記のようになっています。

|引数|変数|説明|
|:-----------|:-----------|:-----------|
|1|Context context|ExpandableListViewが関連付けられるContext|
|2|List< ? extends Map> groupData|Group(親)のリスト|
|3|int expandedGroupLayout|Group(親)のレイアウト|
|4|String[] groupFrom|Group(親)のリストで表示するMapのキー|
|5|int[] groupTo|Group(親)のレイアウト内での文字を表示するTextViewのID|
|6|List< ? extends List< ? extends Map< String, ?>>>|Child(子)のリスト|
|7|int childLayout|Child(子)のレイアウト|
|8|String[] childFrom|Child(子)のリストで表示するMapのキー|
|9|int[] childTo|Child(子)のレイアウト内での文字を表示するTextViewのID|

##リストの項目がクリックされた時の処理

リストの項目がクリックされた時の処理を作るには、OnChildClickListenerインターフェースのonChildClickメソッドを使います。

```java:MainActivity.java
// リスト項目がクリックされた時の処理
lv.setOnChildClickListener(new OnChildClickListener() {
	@Override
	public boolean onChildClick(ExpandableListView parent, View view,
			int groupPosition, int childPosition, long id) {
		ExpandableListAdapter adapter = parent.getExpandableListAdapter();
		// クリックされた場所の内容情報を取得
		@SuppressWarnings("unchecked")
		Map<String, String> item = (Map<String, String>)adapter.getChild(groupPosition, childPosition);
		// アラート表示
        AlertDialog.Builder dlg = new AlertDialog.Builder(MainActivity.this);
        dlg.setTitle("クリック！")
        .setMessage(item.get(KEY1))
        .setPositiveButton("OK", null)
        .show();
        
		return false;
	}
});
```

onChildClickメソッドの引数は下記のようになっています。

|引数|変数|説明|
|:-----------|:-----------|:-----------|
|1|ExpandableListView parent|イベントが起きたExpandableListView|
|2|View view|選択されたリスト項目|
|3|int groupPosition|選択されたリスト項目の親グループの位置（０～）|
|4|int childPosition|選択されたリスト項目の、グループ内での位置（０～）|
|5|long id|選択されたリスト項目のIDを示す値（０～）|

##グループの親項目がクリックされた時の処理

グループの親項目が選択された時の処理を作るには、OnGroupClickListenerインターフェースのonGroupClickメソッドを使います。

```java:MainActivity.java
// グループの親項目がクリックされた時の処理
lv.setOnGroupClickListener(new OnGroupClickListener() {
	@Override
	public boolean onGroupClick(ExpandableListView parent, View view,
			int groupPosition, long id) {
		ExpandableListAdapter adapter = parent.getExpandableListAdapter();
		// クリックされた場所の内容情報を取得
		@SuppressWarnings("unchecked")
		Map<String, String> item = (Map<String, String>)adapter.getGroup(groupPosition);
		// アラート表示
		AlertDialog.Builder dlg = new AlertDialog.Builder(MainActivity.this);
        dlg.setTitle("クリック！")
        .setMessage(item.get(KEY1))
        .setPositiveButton("OK", null)
        .show();
        
		return false;
	}
});
```

onGroupClickメソッドの引数は下記のようになっています。

|引数|変数|説明|
|:-----------|:-----------|:-----------|
|1|ExpandableListView parent|イベントが起きたExpandableListView|
|2|View view|選択されたグループの親項目|
|3|int groupPosition|選択されたグループの親項目の位置（０～）|
|4|long id|選択されたグループの親項目のIDを示す値（０〜）|

#完成ソース

上記の処理をまとめると、下記のようなソースになります。

```java:MainActivity.java
package com.yuuweb.expandablelistviewtest;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.os.Bundle;
import android.view.View;
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.ExpandableListView.OnGroupClickListener;
import android.widget.SimpleExpandableListAdapter;
import android.app.Activity;
import android.app.AlertDialog;

public class MainActivity extends Activity {
	
	private final String KEY1 = "TITLE";
	private final String KEY2 = "SUMMARY";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		int PARENT_DATA = 3;
		// 親ノードのリスト
		List<Map<String, String>> parentList = new ArrayList<Map<String, String>>();
		// 親ノードに表示する内容を生成
		for (int i = 0; i < PARENT_DATA; i++) {
		    Map<String, String> parentData = new HashMap<String, String>();
		    parentData.put(KEY1, "タイトル" + Integer.toString(i));
		    // 親ノードのリストに内容を格納
		    parentList.add(parentData);
		}
		
		int CHILD_DATA = 3;
		// 全体の子ノードのリスト
		List<List<Map<String, String>>> allChildList = new ArrayList<List<Map<String, String>>>();

		// 子要素として表示する文字を生成
		for (int i = 0; i < PARENT_DATA; i++) {
			// 子要素全体用のリスト
			List<Map<String, String>> childList = new ArrayList<Map<String, String>>();

			// 各子ノード用データ格納
			for (int j = 0; j < CHILD_DATA; j++) {
				Map<String, String> childData = new HashMap<String, String>();
				childData.put(KEY1, "子要素" + Integer.toString(j));
				childData.put(KEY2, "概要" + Integer.toString(j));
				// 子ノードのリストに文字を格納
				childList.add(childData);
			}
			// 全体の子ノードリストに各小ノードリストのデータを格納
			allChildList.add(childList);
		}
		
		// アダプタを作る
		SimpleExpandableListAdapter adapter =
				new SimpleExpandableListAdapter(
						this,
						parentList,
						android.R.layout.simple_expandable_list_item_1,
						new String[] { KEY1 },
						new int[] { android.R.id.text1, android.R.id.text2 },
						allChildList,
						android.R.layout.simple_expandable_list_item_2,
						new String[] { KEY1, KEY2 },
						new int[] { android.R.id.text1, android.R.id.text2 }
		);
		//生成した情報をセット
		ExpandableListView lv = (ExpandableListView)findViewById(R.id.expandableListView1);
		lv.setAdapter(adapter);
		
		// リスト項目がクリックされた時の処理
		lv.setOnChildClickListener(new OnChildClickListener() {
			@Override
			public boolean onChildClick(ExpandableListView parent, View view,
					int groupPosition, int childPosition, long id) {
				ExpandableListAdapter adapter = parent.getExpandableListAdapter();
				// クリックされた場所の内容情報を取得
				@SuppressWarnings("unchecked")
				Map<String, String> item = (Map<String, String>)adapter.getChild(groupPosition, childPosition);
				// アラート表示
		        AlertDialog.Builder dlg = new AlertDialog.Builder(MainActivity.this);
		        dlg.setTitle("クリック！")
		        .setMessage(item.get(KEY1))
		        .setPositiveButton("OK", null)
		        .show();
		        
				return false;
			}
		});
		
		// グループの親項目がクリックされた時の処理
		lv.setOnGroupClickListener(new OnGroupClickListener() {
			@Override
			public boolean onGroupClick(ExpandableListView parent, View view,
					int groupPosition, long id) {
				ExpandableListAdapter adapter = parent.getExpandableListAdapter();
				// クリックされた場所の内容情報を取得
				@SuppressWarnings("unchecked")
				Map<String, String> item = (Map<String, String>)adapter.getGroup(groupPosition);
				// アラート表示
				AlertDialog.Builder dlg = new AlertDialog.Builder(MainActivity.this);
		        dlg.setTitle("クリック！")
		        .setMessage(item.get(KEY1))
		        .setPositiveButton("OK", null)
		        .show();
		        
				return false;
			}
		});
	}

}

```

以上です。