Android Tips #19 ListViewのアイテムでラジオボタン付きのカスタムレイアウトを使う

2012.10.10

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

今回はラジオボタンを使った単一選択型の ListView の作りかたを解説したいと思います。 ListViewの基本的な使いかたはこちらを参考にしてください。 下図のような ListView を作ってみたいと思います。

RadioButton付きのアイテムの作りかた

アイテムにはカスタムViewを使うので、まずはカスタムViewに適用するレイアウトを作成します。 今回は ImageView と TextView と RadioButton が含まれた LinearLayout にしてみました。 注意点は RadioButton の android:focusable プロパティと android:clickable プロパティを false にする ところです。この2つのプロパティが true だとアイテムをタップしたときに RadioButton が先にハンドリングしてしまうのでアイテム選択の処理が走りません。

custom_item_view.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="match_parent"
	android:layout_height="wrap_content"
	android:gravity="center_vertical"
	android:paddingLeft="10dp"
	android:paddingRight="10dp">

	<ImageView
		android:layout_width="50dp"
		android:layout_height="50dp"
		android:padding="4dp"
		android:src="@drawable/ic_launcher"
		 />

	<TextView
		android:id="@+id/text_view1"
		android:layout_width="0dp"
		android:layout_height="wrap_content"
		android:layout_weight="1"
		android:textSize="18sp"
		android:textColor="@android:color/black" />

	<RadioButton
		android:id="@+id/radio_button"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:focusable="false"
		android:clickable="false"
		 />

</LinearLayout>

次にカスタムViewクラスを作ります。カスタムViewのコンストラクタで上記レイアウトを addView() で子として追加します。 ポイントはCheckable インターフェースを実装するというところです。アイテムの View が Checkable インターフェースを実装していると、ListView は選択式のアイテムとして処理してくれます。Checkable インターフェースには isChecked() メソッド、 setChecked() メソッド、 toggle() メソッドがあります。これらのメソッドは ListView でアイテムが選択状態になったときに呼び出されるので、このメソッドを利用し setChecked() メソッド内で RadioButton の setChecked() を変更します。 すると、アイテムが選択されたときに RadioButton の表示を選択状態に切り替えることができます。

CustomItemView.java

package jp.classmethod.android.sample.checkablelistview;

import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.widget.Checkable;
import android.widget.FrameLayout;
import android.widget.RadioButton;

public class CustomItemView extends FrameLayout implements Checkable {

	private RadioButton mRadioButton;
	
	public CustomItemView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		initialize();
	}

	public CustomItemView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initialize();
	}

	public CustomItemView(Context context) {
		super(context);
		initialize();
	}
	
	private void initialize() {
		// レイアウトを追加する
		addView(inflate(getContext(), R.layout.custom_item_view, null));
		mRadioButton = (RadioButton) findViewById(R.id.radio_button);
	}

	@Override
	public boolean isChecked() {
		return mRadioButton.isChecked();
	}

	@Override
	public void setChecked(boolean checked) {
		// RadioButton の表示を切り替える
		mRadioButton.setChecked(checked);
	}

	@Override
	public void toggle() {
	}

}

最後に、この View を Adapter で使うためにレイアウトを定義します。あとは ListView の setChoiceMode() を CHOICE_MODE_SINGLE (単一選択モード)にすれば完成です。

list_view_item.xml

<?xml version="1.0" encoding="utf-8"?>
<jp.classmethod.android.sample.checkablelistview.CustomItemView
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:layout_width="match_parent"
	android:layout_height="match_parent" />

MainActivity.java

package jp.classmethod.android.sample.checkablelistview;

import java.util.ArrayList;
import java.util.HashMap;

import android.app.Activity;
import android.os.Bundle;
import android.widget.AbsListView;
import android.widget.ListView;
import android.widget.SimpleAdapter;

public class MainActivity extends Activity {

	private ListView mListView;
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		SimpleAdapter adapter = new SimpleAdapter(getApplicationContext(),
				getItemList(), R.layout.list_view_item,
				new String[] { "title" }, new int[] { R.id.text_view1 });
		
		ListView listView = new ListView(getApplicationContext());
		setContentView(listView);
		listView.setAdapter(adapter);
		// 単一選択モードにする
		listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
		// デフォルト値をセットする
		listView.setItemChecked(0, true);
	}
	
	private ArrayList<HashMap<String, String>> getItemList() {
		ArrayList<HashMap<String, String>> list = new ArrayList<HashMap<String, String>>();
		HashMap<String, String> item1 = new HashMap<String, String>();
		item1.put("title", "リュウレンジャー");
		list.add(item1);
		HashMap<String, String> item2 = new HashMap<String, String>();
		item2.put("title", "シシレンジャー");
		list.add(item2);
		HashMap<String, String> item3 = new HashMap<String, String>();
		item3.put("title", "テンマレンジャー");
		list.add(item3);
		HashMap<String, String> item4 = new HashMap<String, String>();
		item4.put("title", "キリンレンジャー");
		list.add(item4);
		HashMap<String, String> item5 = new HashMap<String, String>();
		item5.put("title", "ホウオウレンジャー");
		list.add(item5);
		HashMap<String, String> item6 = new HashMap<String, String>();
		item6.put("title", "キバレンジャー");
		list.add(item6);
		return list;
	}
}

実行結果

また、setChecked() メソッド内に処理を書き足せば RadioButton 以外の View も自由に変更することができます。 以下の例では背景と文字色を変更してみました。

@Override
public void setChecked(boolean checked) {
	// RadioButton の表示を切り替える
	mRadioButton.setChecked(checked);
	TextView textView = (TextView) findViewById(R.id.text_view1);
	textView.setTextColor(checked ? Color.WHITE : Color.BLACK);
	setBackgroundColor(checked ? Color.DKGRAY : Color.TRANSPARENT);
}

実行結果

ListView を Spinner に置き換えることもできます!

@Override
public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);

	SimpleAdapter adapter = new SimpleAdapter(getApplicationContext(),
			getItemList(), R.layout.list_view_item,
			new String[] { "title" }, new int[] { R.id.text_view1 });

	// Spinner の場合
	setContentView(R.layout.layout_main);
	Spinner spinner = (Spinner) findViewById(R.id.spinner);
	spinner.setAdapter(adapter);
}

実行結果

ソースコード

今回実装したソースコードを github で共有しています!ぜひ参考にしていただければと思います。

suwa-yuki/CheckableListViewSample

まとめ

今回は単一選択型の ListView でカスタムレイアウトを使う方法をご紹介しましたが、 複数選択型(チェックボックス付き)の ListView もほぼ同じ方法で実装することができます。変わるところは カスタムView の RadioButton が CheckBox になる点と、 ListView の setChoiceMode() の引数が CHOICE_MODE_MULTIPLE になる点くらいです。
よく必要になる実装だと思うので、参考にしていただければ幸いです。また AlertDialog でも実装できるので、応用編としてぜひ試してみてください。