Android4.0で複数選択モードを利用する/Getting Started


AndroidではListViewを利用してデータを一覧表示することができます。
さらにListViewでは、フラグを変更することで簡単に選択モードを変更することができます。
(ListViewの選択モードを切り替える参照)

上記リンクでは3つの選択モード(選択なし、一つのみ選択、複数選択)を解説していますが、Android3.0から新たにもう一つ選択モードが追加されました。
本記事内ではそのモードを便宜的に「複数選択アクションモード」と呼ぶようにします。今回はこの複数選択アクションモードについて解説します。
複数選択アクションモードの動作は以下のようになっています(画像はAndroid 4.0のエミュレータです。Android 4.0でも引き続きサポートされています)。

上図左(初期状態)でリストの項目を長押しすると、アクションモードと呼ばれる複数選択モードに遷移します(上図真ん中)。
アクションモードの状態で複数選択し、左上のチェック(決定)ボタンを押すことで選択を決定します。
複数選択アクションモードを用いることで、これまで自分で実装する必要があった複数選択後の決定処理を、フレームワークを用いて実装することができます。
また複数選択アクションモードでは、アクションモードへの遷移時や項目選択時にイベントが発生します。
これらイベントをコールバックメソッドを用いることで取得し、イベント発生のタイミングで処理を行うことも可能です。
さらにAndroid3.0から追加されたアクションバーとも連携が可能です。

今回解説に用いるサンプルではこの複数選択アクションモードを用いて、選択した項目の文字列を連結し、画面遷移後に表示したいと思います。

ポイントは、

  • ListViewクラスのsetChoiceModeメソッドにCHOICE_MODE_MULTIPLE_MODALフラグをセットする
  • ListViewクラスのsetMultiChoiceModeListenerメソッドを用いてリスナーを登録する
  • 決定ボタン押下時のイベント取得

の3つです。

それでは続きをどうぞ

フラグおよびリスナーのセット

まずはListViewの実装とCHOICE_MODE_MULTIPLE_MODALフラグのセット、およびリスナーの実装を行います。
なお、アクティビティーにはListActivityを用います。

以下のソースコードを見てみましょう。
MultipleListActivityActivity.java

public class MultipleListActivityActivity extends ListActivity {
	static final String[] GENRES = new String[] { "mhidaka", "kacchi0516", "rongon_xp", "kobashinG", "seit", "kei_i_t", "furusin_oriver","muchiki0226" };

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

        ListView listView = getListView();
        listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
        listView.setMultiChoiceModeListener(new Callback());
        listView.setAdapter(new ArrayAdapter(this,android.R.layout.simple_list_item_checked, GENRES));
    }
(...後述...)
}

9行目でListViewクラスのsetChoiceModeメソッドを用いて、CHOICE_MODE_MULTIPLE_MODALフラグをセットしています。
ただし、単にフラグをセットしただけではアクションモードには遷移しません。
10行目に記述しているように、ListViewクラスのsetMultiChoiceModeListenerメソッドを用いてリスナーを登録し、各種イベントを取得できるようにする必要があります。
また、11行目では、ListViewクラスのsetAdapterメソッドを用いて、リストの項目および各項目のレイアウトを指定しています。
(本題からはずれますが)今回利用する項目のレイアウトはAndroidのフレームワークにて標準で定義されているレイアウトを用いています。
simple_list_item_checked.xml

<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@android:id/text1"
 android:layout_width="match_parent"
 android:layout_height="?android:attr/listPreferredItemHeight"
 android:textAppearance="?android:attr/textAppearanceLarge"
 android:gravity="center_vertical"
 android:checkMark="?android:attr/listChoiceIndicatorMultiple"
 android:paddingLeft="6dip"
 android:paddingRight="6dip"
/>

下図のように、TextViewとチェックボックスを組み合わせたようなレイアウトになっています。


(CheckedTextViewについては、 Spinnerクラスを使ってコンボボックスを表示する でも触れていますので参考にして下さい)。
Android標準のレイアウトの一覧は公式サイトで確認できます。実際には「<android-sdkルート>/platforms/android-xx/data/res/layout」にて各種定義されています。
それでは本題に戻って、次の節でリスナーの中身を見ていきます。

リスナーを実装する

複数選択アクションモードを使用する上で重要なのは、以下の表に示すコールバックメソッドたちです。
[table “215” not found /]

これらコールバックメソッドを実装することで、アクションモードを利用することができます。
以下のソースコードを見てみましょう。
MultipleListActivityActivity.java

    private class Callback implements ListView.MultiChoiceModeListener {
			@Override
			public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
				// アクションアイテム選択時
				return true;
			}

			@Override
			public boolean onCreateActionMode(ActionMode mode, Menu menu) {
				// アクションモード初期化処理
				return true;
			}

			@Override
			public void onDestroyActionMode(ActionMode mode) {
				// 決定ボタン押下時
				final int checkedCount = getListView().getCheckedItemCount();
				SparseBooleanArray list = getListView().getCheckedItemPositions();
				String str = "";
				for(int i=0;i<GENRES.length;i++){
					boolean checked = list.get(i);
					if (checked == true){
						str = str+GENRES[i] + " ";
					}
				}

				Intent intent = new Intent(getApplicationContext(), ResultDisp.class);
				intent.putExtra("checked_list", str);
				startActivity(intent);
			}

			@Override
			public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
				// アクションモード表示事前処理
				return true;
			}

			@Override
			public void onItemCheckedStateChanged(ActionMode mode,
					int position, long id, boolean checked) {
				// アクションモード時のアイテムの選択状態変更時
			}
    }

各種イベントを取得するにはまず、MultiChoiceModeListenerをimplementsしたcallbackクラスを用意します。
callbackクラスの中に、先ほど表に示した各種コールバックメソッドを実装します。
ここで一つ重要なポイントは、onCreateActionModeメソッド、onPrepareActionModeメソッドおよびonActionItemClickedメソッド内でtureを返している点です。
trueを返すことで処理が正常に完了していることを通知しています。Falseを返してしまうと、その後のイベントを取得できませんので注意が必要です。

今回は、最も利用されるであろう決定ボタン押下時に呼ばれるonDestroyActionModeメソッド内に処理を記述してみました。
onDestroyActionModeメソッド内ではまず、ListViewクラスのgetCheckedItemCountメソッドを用いて、選択されているアイテムの個数を取得しています。
次に、ListViewクラスのgetCheckedItemPositionsメソッドを用いて、何番目のアイテムが選択されているかという情報を持ったリストを取得しています。(例えば3番目の項目が選択されていた場合には、リストの3番目に「true」が入っています。)
20〜25行目で、配列(GENRES)から選択された項目の文字列を取得し、連結しています。

最後に、27〜29行目で生成した文字列をIntentで次の画面(ResultDispアクティビティー)に渡しています。

ResultDispアクティビティーでは、受け取った文字列を表示しています。

ResultDisp.java

public class ResultDisp extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.disp);

        TextView tv = (TextView)findViewById(R.id.result);
        // Intentから文字列取り出し
        String str = getIntent().getExtras().getString("checked_list").toString();
        // TextViewに反映
        tv.setText(str);
    }
}

結果は下図のようになります。

選択された項目の文字列が、次画面にて連結されて表示されています。

まとめ

今回解説した複数選択アクションモードは、複数選択リストをユーザーインターフェース的にもきれいに実装することができます。
ただし、最初に長押しする必要があるというのが、ユーザー的に少々わかりにくいです。
画面遷移時にトーストを表示するなど、長押しを促すような工夫が必要かもしれません。