ActionBarのメニューにCheckBoxを表示する

前置き

ActionBarでは、メニューをすべて表示することができなかったり"showAsAction="never"を設定したメニューがある場合、一部のメニューをActionBar右端のドロップダウンに格納することがあります。 Andorid Developersではこの機能をAction Overflowと呼んでいます。 Action Overflowに関する細かい機能名称などが見つけられなかったため、この記事ではオーバーフローに格納されているメニューを「オーバーフローメニュー」と呼ぶことにします。 http://developer.android.com/design/patterns/actionbar.html

オーバーフローメニューの活用

ActionBarのオーバーフローメニューにはチェックボックスラジオボタンを表示することができます。 メニューと違って常時表示されないデメリットはありますが、タイトルが表示されるのでどのような機能かわかりやすく、アイコンを用意しなくても良いというメリットもあります。

下の画像はPlayストアアプリでの例です。 Playストアでの例

以下、チェックボックスラジオボタン、それぞれの実装について説明します。 サンプルではappcompat-v7版のActionBarを使用しています。

チェックボックスの実装

チェックボックスを利用する際、オーバーフローメニュー以外ではチェックボックスが表示されない事に気をつけてください。 <item>要素にshowAsAction="never"を設定しておくことで確実にオーバーフローメニューにすることができますが、 showAsAction="always"showAsAction="ifRoom"を定義するとチェックボックスが表示されなくなります。 チェックボックス自体はメニューを定義しているxmlandroid:checkable="true"を加えるだけで表示されます。 最初からチェック済みにしたい場合はandroid:checked="true"を定義してください。

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MyActivity" >
    <item android:id="@+id/action_checkbox"
        android:title="チェックボックス"
        android:checkable="true"
        app:showAsAction="never" />
</menu>

最も注意すべき点として、チェックボックスの状態変更をソースコード上で行う必要があることに気をつけてください。 また、チェックボックスの状態はMenuItem.isChecked()で取得できますが、ユーザ操作がチェックボックスに反映された後かどうかを意識する必要があります。

````MyActivity.java @Override public boolean onCreateOptionsMenu(Menu menu) { // メニュー生成 getMenuInflater().inflate(R.menu.menu, menu); return true; }

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();
    if (id == R.id.action_checkbox) {
        // チェックボックスの状態変更を行う
        item.setChecked(!item.isChecked());
        // 反映後の状態を取得する
        boolean checked = item.isChecked();
        return true;
    }
    return super.onOptionsItemSelected(item);
}

````

以下の様な表示になります。 Screenshot_2014-08-31-15-13-57.png

ラジオボタンの実装

ラジオボタンを使うといくつかのオーバーフローメニューからひとつを選ばせるようなUIを作ることができます。 チェックボックス同様、オーバーフローメニュー以外ではラジオボタンが表示されないことに注意してください。 各<item>要素にはshowAsAction="never"を指定する必要があります。

ラジオボタンでは、選択肢となる<item>要素の親として<group>を定義します。 この<group>要素にandroid:checkableBehavior="single"を定義することで、checked状態になる<item>要素をひとつだけに限定することができます。 このとき、各<item>要素にはandroid:checkable="true"を定義するとチェックボックスが表示されてしまうので注意してください。 また、特定の<item>を最初から選択状態にしたい場合はその<item>android:checked="true"を定義してください。

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MyActivity">

    <group android:checkableBehavior="single">
        <item
            android:id="@+id/action_radio1"
            android:title="ラジオ1"
            app:showAsAction="never" />
        <item
            android:id="@+id/action_radio2"
            android:title="ラジオ2"
            app:showAsAction="never" />
    </group>

</menu>

ラジオボタンでもチェックボックス同様、状態変更をソースコード上で行う必要があることに気をつけてください。 ただし、android:checkableBehavior="single"を定義している場合、<group>内のどれかひとつがMenuItem#setChecked(true)されたときに他のMenuItemMenuItem#setChecked(false)された状態になるため、選択された要素の状態のみ反映すれば良いことになります。

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        switch (id){
            case R.id.action_radio1:
                item.setChecked(!item.isChecked());
                Toast.makeText(this,"action_radio1",Toast.LENGTH_SHORT).show();
                return true;
            case R.id.action_radio2:
                item.setChecked(!item.isChecked());
                Toast.makeText(this,"action_radio2",Toast.LENGTH_SHORT).show();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

以下のような表示になります。 Screenshot_2014-08-31-15-30-21.png