メニュー

メニューは、さまざまなタイプのアプリに共通のユーザー インターフェース コンポーネントです。使い慣れた一貫したユーザー エクスペリエンスを提供するには、Menu API を使ってユーザーのアクションやその他のオプションをアクティビティに表示する必要があります。

Android 3.0(API レベル 11)以降では、Android 搭載デバイスで専用のメニューボタンを表示する必要はなくなりました。この変更により、Android アプリでは従来の 6 項目のメニューパネルを使うのではなく、一般的なユーザー アクションを表示するアプリバーを提供することが推奨されます。

一部のメニュー項目のデザインとユーザー エクスペリエンスは変更されましたが、一連のアクションとオプションを定義するセマンティクスは、引き続き Menu API に基づきます。このガイドでは、すべてのバージョンの Android で表示される 3 種類の基本的なメニューやアクションを作成する方法について説明します。

オプション メニューとアプリバー
オプション メニューは、アクティビティの主なメニュー項目が配置されたメニューです。オプション メニューには、「検索」、「メールの作成」、「設定」などの、アプリに広範囲に影響するアクションを配置する必要があります。

詳しくは、オプション メニューの作成のセクションをご覧ください。

コンテキスト メニューとコンテキスト アクション モード
コンテキスト メニューは、ユーザーが要素を長押しすると表示されるフローティング メニューです。ここでは、選択したコンテンツやコンテキスト フレームに影響するアクションが提供されます。

コンテキスト アクション モードでは、画面最上部にあるバーで選択されたコンテンツに影響するアクション項目が表示され、ユーザーは複数の項目を選択できます。

詳しくは、コンテキスト メニューの作成のセクションをご覧ください。

ポップアップ メニュー
ポップアップ メニューでは、メニューを呼び出すビューに固定された縦方向のリストで項目が一覧表示されます。特定のコンテンツに関連するアクションの概要を表示する場合や、コマンドの 2 番目の部分のオプションを表示する場合に適しています。ポップアップ メニューのアクションは対応するコンテンツに直接影響を与えないようにしてください(そのためにコンテキスト アクションがあります)。ポップアップ メニューは、アクティビティのコンテンツ領域に関連する拡張されたアクション用です。

詳しくは、ポップアップ メニューの作成のセクションをご覧ください。

XML でのメニューの定義

Android では、すべてのメニュータイプに、メニュー項目を定義するための標準の XML 形式が提供されます。 アクティビティのコードでメニューをビルドするのではなく、メニューとそのすべての項目を XML メニュー リソースで定義してください。その後、アクティビティまたはフラグメントでメニュー リソースをインフレートできます(Menu オブジェクトとして読み込みます)。

メニュー リソースを使うことは、次のような理由で優れた方法と言えます。

  • XML でメニュー構造の視覚化が簡単になる。
  • メニューのコンテンツを、アプリの振る舞いコードと区別する。
  • 別のプラットフォーム バージョン、画面サイズ、その他の構成のために、アプリリソース フレームワークを活用して、代替のメニュー構成を作成できる。

メニューを定義するには、プロジェクトの res/menu/ ディレクトリ内で XML ファイルを作成し、次の要素を含むメニューをビルドします。

<menu>
メニュー項目のコンテナである Menu を定義します。<menu> 要素は、ファイルのルートノードである必要があります。また、1 つ以上の <item> 要素と <group> 要素を持つことができます。
<item>
メニュー内の 1 つの項目を表す MenuItem を作成します。この要素には、サブメニューを作成するために、ネストされた <menu> 要素を含めることができます。
<group>
省略可能な <item> 要素の非表示コンテナ。メニュー項目を分類して、メニュー項目でアクティブ状態や可視性などのプロパティを共有できるようにします。詳細については、メニュー グループの作成のセッションをご覧ください。

game_menu.xml という名前のメニューの例を次に示します。

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/new_game"
          android:icon="@drawable/ic_new_game"
          android:title="@string/new_game"
          android:showAsAction="ifRoom"/>
    <item android:id="@+id/help"
          android:icon="@drawable/ic_help"
          android:title="@string/help" />
</menu>

<item> 要素では、項目の外観と動作を定義するために使用できるいくつかの属性がサポートされています。上記メニューの項目には、次の属性が含まれます。

android:id
項目固有のリソース ID。ユーザーが項目を選択するときに、アプリがその項目を認識できるようにします。
android:icon
ドローアブルへの参照。アイテムのアイコンとして使います。
android:title
文字列への参照。アイテムのタイトルとして使用します。
android:showAsAction
該当の項目がアプリバーのアクション項目として、いつ、どのように表示される必要があるのかを指定します。

これらの属性は使用する必要のある代表的なものですが、他にもさまざまな属性があります。 サポートされている属性のすべてについては、メニュー リソースのドキュメントをご覧ください。

項目にサブメニューを追加するには、各メニューで、<item> の子として <menu> 要素を追加します。PC アプリケーションのメニューバー(ファイル、編集、表示など)にある項目のように、トピックで分類できる多くの機能がアプリに備わっている場合は、サブメニューが役立ちます。次に例を示します。

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/file"
          android:title="@string/file" >
        <!-- "file" submenu -->
        <menu>
            <item android:id="@+id/create_new"
                  android:title="@string/create_new" />
            <item android:id="@+id/open"
                  android:title="@string/open" />
        </menu>
    </item>
</menu>

アクティビティでメニューを使うには、MenuInflater.inflate() を使って、メニュー リソースをインフレートする(XML リソースをプログラム可能なオブジェクトに変換する)必要があります。次のセクションでは、メニューのタイプごとにメニューをインフレートする方法を説明します。

オプション メニューの作成

図 1. ブラウザでのオプションメニュー

オプション メニューには、「検索」、「メールの作成」、「設定」などの、現在のアクティビティ コンテキストに関連するアクションとその他のオプションを配置します。

オプション メニューの項目が画面上のどこに表示されるかは、開発対象のアプリのバージョンによって異なります。

  • Android 2.3.x(API レベル 10)以前向けにアプリを開発した場合、ユーザーがメニューボタンを押すと、図 1 のようにオプション メニューのコンテンツが画面の上部に表示されます。開いて最初に表示される部分は、最大 6 つのメニュー項目で構成されるアイコン メニューです。7 つ以上のメニュー項目が含まれている場合、Android では 6 番目以降の項目がオーバーフロー メニューに配置されます。これらの項目は、[More] を選択して開くことができます。
  • Android 3.0(API レベル 11)以降向けにアプリを開発した場合、オプション メニューの項目はアプリバーに表示されます。デフォルトでは、システムによってアクション オーバーフローにすべての項目が配置され、ユーザーはアプリバー右端にあるアクション オーバーフロー アイコンを使ってその項目を表示できます。デバイスにメニューボタンがある場合は、そのボタンを押して表示できます。重要なアクションにすみやかにアクセスできるようにするには、項目がいくつかアプリバーに表示されるよう、対応する <item> 要素に android:showAsAction="ifRoom" を追加します(図 2 を参照)。

    アクション項目とその他のアプリバーの動作について詳しくは、トレーニング クラスのアプリバーの追加をご覧ください。

図 2.アクション オーバーフロー ボタンなどのボタンがいくつか表示されている Google スプレッドシート アプリ

Activity サブクラスや Fragment サブクラスのいずれかからオプション メニューの項目を宣言できます。アクティビティとフラグメントの両方でオプション メニューの項目を宣言する場合、それらは UI に統合されます。まず、アクティビティの項目が表示され、次にアクティビティに各フラグメントが追加される順序で各フラグメントの項目が表示されます。必要に応じて、移動する必要のある各 <item>android:orderInCategory 属性を使ってメニュー項目の順序を並べ替えることができます。

アクティビティのオプション メニューを指定するには、onCreateOptionsMenu() をオーバーライドします(フラグメントは独自の onCreateOptionsMenu() コールバックを提供します)。このメソッドでは、メニュー リソース(XML で定義されます)を、コールバックで提供される Menu にインフレートできます。次に例を示します。

Kotlin

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    val inflater: MenuInflater = menuInflater
    inflater.inflate(R.menu.game_menu, menu)
    return true
}

Java

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

add() を使ってメニュー項目を追加し、findItem() で項目を取得して MenuItem API でそのプロパティを修正することもできます。

Android 2.3.x 以前向けにアプリを開発した場合、ユーザーが初めてメニューを開いたときに、システムは onCreateOptionsMenu() を呼び出してオプション メニューを作成します。Android 3.0 以降向けに開発した場合、アクティビティの開始時にシステムが onCreateOptionsMenu() を呼び出し、アプリバーに項目が表示されるようにします。

クリック イベントの処理

ユーザーがオプション メニューの項目(アプリバーのアクション項目など)を選択すると、アクティビティの onOptionsItemSelected() メソッドが呼び出されます。このメソッドでは、選択された MenuItem が渡されます。項目を識別するため、getItemId() を呼び出します。これにより、メニュー項目に対して一意の ID が返されます(この ID は、メニュー リソースで android:id 属性によって定義されるか、add() メソッドに指定した整数によって定義されています)。この ID を既知のメニュー項目と照合して適切なアクションを実行できます。次に例を示します。

Kotlin

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    // Handle item selection
    return when (item.itemId) {
        R.id.new_game -> {
            newGame()
            true
        }
        R.id.help -> {
            showHelp()
            true
        }
        else -> super.onOptionsItemSelected(item)
    }
}

Java

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle item selection
    switch (item.getItemId()) {
        case R.id.new_game:
            newGame();
            return true;
        case R.id.help:
            showHelp();
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

メニュー項目を正常に処理する場合、true を返します。メニュー項目を処理しない場合は、onOptionsItemSelected() のスーパークラスの実装を呼び出す必要があります(デフォルトの実装では false を返します)。

アクティビティにフラグメントが含まれる場合、システムはまずアクティビティに対して onOptionsItemSelected() を呼び出します。次に true が返されるまで、またはすべてのフラグメントが呼び出されるまで、各フラグメントが追加された順序で、各フラグメントに対してこのメソッドを呼び出します。

ヒント: Android 3.0 では、android:onClick 属性を使って、メニュー項目のクリック動作を XML で定義できるようになっています。その属性値は、メニューを使ってアクティビティによって定義されるメソッドの名前にする必要があります。そのメソッドは、パブリックであり、1 つの MenuItem パラメータを受け入れる必要があります。システムがこのメソッドを呼び出すと、選択したメニュー項目が渡されます。詳細と例については、メニュー リソースのドキュメントをご覧ください。

ヒント: アプリに複数のアクティビティが含まれていて、その一部で同じオプション メニューが提供されている場合、onCreateOptionsMenu() メソッドと onOptionsItemSelected() メソッドのみを実装するアクティビティを作成することを検討します。その後、同じオプション メニューを共有する必要のある各アクティビティのこのクラスを拡張します。このように、メニュー アクションを処理するためのコードを 1 セットで管理し、それぞれの子 class クラスはメニューの動作を継承できます。子孫アクティビティの 1 つにメニュー項目を追加する場合は、そのアクティビティの onCreateOptionsMenu() をオーバーライドします。元のメニュー項目が作成されるように super.onCreateOptionsMenu(menu) を呼び出し、menu.add() で新しいメニュー項目を追加します。個々のメニュー項目のスーパークラスの動作をオーバーライドすることもできます。

実行時におけるメニュー項目の変更

システムが onCreateOptionsMenu() を呼び出した後、データを入力する Menu のインスタンスは残り、なんらかの理由でメニューが無効にならない限り、onCreateOptionsMenu() をもう一度呼び出すことはありません。ただし、初期のメニュー状態を作成し、アクティビティのライフサイクル中に変更しないという目的の場合に限って、onCreateOptionsMenu() を使う必要があります。

アクティビティのライフサイクル中に発生するイベントに基づいてオプション メニューを変更する場合は、onPrepareOptionsMenu() メソッドで変更できます。このメソッドは、現在存在している Menu オブジェクトを渡し、それを変更(項目の追加、削除、無効化など)できるようにします(フラグメントでも onPrepareOptionsMenu() コールバックが提供されます)。

Android 2.3.x 以前では、ユーザーがメニューボタンを押してオプション メニューを開くたびに、システムによって onPrepareOptionsMenu() が呼び出されます。

Android 3.0 以降では、メニュー項目がアプリバーに表示されるときに、オプション メニューは常に開かれているとみなされます。イベントが発生し、メニューを更新するときは、invalidateOptionsMenu() を呼び出して、システムで onPrepareOptionsMenu() を呼び出すようリクエストする必要があります。

注: 現在フォーカスされている View に基づいてオプション メニューの項目を変更しないでください。タッチモードの場合(ユーザーがトラックボールや D-pad を使わないとき)、ビューはフォーカスを取得できないため、オプション メニューの項目変更の基礎としてフォーカスを使用しないでください。View に状況依存のメニュー項目を提供する場合は、コンテキスト メニューを使います。

コンテキスト メニューの作成

図 3. フローティング コンテキスト メニュー(左)とコンテキスト アクションバー(右)のスクリーンショット

コンテキスト メニューでは、UI の特定の項目やコンテキスト フレームに影響するアクションが提供されます。コンテキスト メニューは、どのビューにもを使用できますが、大半は ListViewGridView などの、各項目でユーザーが直接実行できるビュー コレクションの項目に使用されます。

コンテキスト アクションを提供するには、次の 2 つの方法があります。

  • フローティング コンテキスト メニュー。メニューは、ユーザーが長押しクリックする(押したままにする)と、コンテキスト メニューのサポートを宣言するビュー上で、メニュー項目のフローティング リストとして表示されます(ダイアログと似ています)。ユーザーは一度に 1 つのアイテムでコンテキスト アクションを実行できます。
  • コンテキスト アクション モード。このモードは、ActionMode のシステム実装であり、選択された項目に影響するアクション項目と一緒にコンテキスト アクションバーを画面最上部に表示します。このモードがアクティブなとき、ユーザーは一度に複数の項目でアクションを実行できます(アプリで許可されている場合)。

注: コンテキスト アクション モードは、Android 3.0(API レベル 11)以降で利用可能であり、コンテキスト アクションを表示するのに適したモードです。アプリが 3.0 より前のバージョンに対応している場合は、そのデバイスではフローティング コンテキスト メニューを使う必要があります。

フローティング コンテキスト メニューの作成

フローティング コンテキスト メニューを提供するには:

  1. registerForContextMenu() を呼び出し、View を渡して、コンテキスト メニューに関連付ける必要のある View を登録します。

    アクティビティで ListViewGridViewが使用され、各項目に同じコンテキスト メニューを提供する場合、ListViewGridViewregisterForContextMenu() に渡してコンテキスト メニューにすべての項目を登録します。

  2. ActivityFragmentonCreateContextMenu() メソッドを実装します。

    登録されたビューが長押しクリック イベントを受け取ると、システムは onCreateContextMenu() メソッドを呼び出します。通常、ここでメニュー リソースをインフレートして、メニュー項目を定義します。次に例を示します。

    Kotlin

    override fun onCreateContextMenu(menu: ContextMenu, v: View,
                            menuInfo: ContextMenu.ContextMenuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo)
        val inflater: MenuInflater = menuInflater
        inflater.inflate(R.menu.context_menu, menu)
    }
    

    Java

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v,
                                    ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.context_menu, menu);
    }
    

    MenuInflater では、メニュー リソースからコンテキスト メニューをインフレートできます。そのコールバック メソッド パラメータには、ユーザーが選択した View と、選択された項目に関する追加情報を提供する ContextMenu.ContextMenuInfo オブジェクトが含まれます。アクティビティに、それぞれ別のコンテキスト メニューを提供するビューが複数ある場合、それらのパラメータを使ってインフレートするコンテキスト メニューを決定できます。

  3. onContextItemSelected() を実装します。

    ユーザーがメニュー項目を選択すると、システムによってこのメソッドが呼び出され、適切なアクションを実行できるようになります。次に例を示します。

    Kotlin

    override fun onContextItemSelected(item: MenuItem): Boolean {
        val info = item.menuInfo as AdapterView.AdapterContextMenuInfo
        return when (item.itemId) {
            R.id.edit -> {
                editNote(info.id)
                true
            }
            R.id.delete -> {
                deleteNote(info.id)
                true
            }
            else -> super.onContextItemSelected(item)
        }
    }
    

    Java

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
        switch (item.getItemId()) {
            case R.id.edit:
                editNote(info.id);
                return true;
            case R.id.delete:
                deleteNote(info.id);
                return true;
            default:
                return super.onContextItemSelected(item);
        }
    }
    

    getItemId() メソッドは、選択されたメニュー項目の ID を照会します。この ID は、XML でのメニューの定義のセクションで説明されているように、android:id 属性を使って XML で各メニュー項目に割り当てる必要があります。

    メニュー項目を正常に処理する場合、true を返します。メニュー項目を処理しない場合は、スーパークラスの実装にメニュー項目を渡す必要があります。アクティビティにフラグメントが含まれる場合、そのアクティビティは最初にこのコールバックを受け取ります。未処理のときにスーパークラスを呼び出すと、システムは truefalse が返されるまで、各フラグメントのそれぞれのコールバック メソッドに 1 つずつ、各フラグメントが追加された順序でイベントを渡します(Activityandroid.app.Fragment のデフォルト実装では false が返されるため、未処理のときは常にスーパークラスを呼び出す必要があります)。

コンテキスト アクション モードの使用

コンテキスト アクション モードは、ActionMode のシステム実装であり、ユーザーによるコンテキスト アクション実行のための操作に焦点が置かれています。ユーザーが項目を選択してこのモードを有効にすると、画面の最上部にコンテキスト アクションバーが表示され、現在選択中の項目で実行できるアクションが表示されます。このモードが有効な間は、ユーザーは複数の項目の選択(許可されている場合)、項目の選択解除、アクティビティ内の継続的な移動(許可されている範囲内で)を行うことができます。ユーザーによるすべての項目の選択解除、戻るボタンの押下、バーの左側での Done アクションの実行が行われると、このアクション モードは無効になり、コンテキスト アクションバーは表示されなくなります。

注: コンテキスト アクションバーをアプリバーと関連付ける必要はありません。コンテキスト アクションバーの表示が、アプリバーの位置にかかる場合でも、個別に操作できます。

コンテキスト アクションを提供するビューでは、通常は次の 2 つのイベントのいずれかまたは両方で、コンテキスト アクション モードを呼び出す必要があります。

  • ユーザーがビューで長押しクリックする。
  • ユーザーがチェックボックスまたは類似の UI コンポーネントをビュー内で選択する。

アプリがどのようにコンテキスト アクション モードを呼び出して各アクションの動作を定義するかは、デザインによって異なります。基本的に次の 2 つのデザインがあります。

  • 個別の任意のビューでのコンテキスト アクション用。
  • ListViewGridView の項目グループでのバッチ コンテキスト アクション用(ユーザーが複数の項目を選択し、そのすべてでアクションを実行できるようにする)。

次のセクションでは、各シナリオに必要な設定について説明します。

個別のビューに対してコンテキスト アクション モードを有効にする

ユーザーが特定のビューを選択したときにのみコンテキスト アクション モードを呼び出すには、次のようにします。

  1. ActionMode.Callback インターフェースを実装します。このコールバック メソッドでは、コンテキスト アクションバーに対するアクションの指定、アクション項目でのクリック イベントへの応答、アクション モードの他のライフサイクル イベントに対する処理を行えます。
  2. ユーザーがビューを長押しクリックしたときなど、バーを表示するときに、startActionMode() を呼び出します。

次に例を示します。

  1. ActionMode.Callback インターフェースを実装します。

    Kotlin

    private val actionModeCallback = object : ActionMode.Callback {
        // Called when the action mode is created; startActionMode() was called
        override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
            // Inflate a menu resource providing context menu items
            val inflater: MenuInflater = mode.menuInflater
            inflater.inflate(R.menu.context_menu, menu)
            return true
        }
    
        // Called each time the action mode is shown. Always called after onCreateActionMode, but
        // may be called multiple times if the mode is invalidated.
        override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
            return false // Return false if nothing is done
        }
    
        // Called when the user selects a contextual menu item
        override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
            return when (item.itemId) {
                R.id.menu_share -> {
                    shareCurrentItem()
                    mode.finish() // Action picked, so close the CAB
                    true
                }
                else -> false
            }
        }
    
        // Called when the user exits the action mode
        override fun onDestroyActionMode(mode: ActionMode) {
            actionMode = null
        }
    }
    

    Java

    private ActionMode.Callback actionModeCallback = new ActionMode.Callback() {
    
        // Called when the action mode is created; startActionMode() was called
        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            // Inflate a menu resource providing context menu items
            MenuInflater inflater = mode.getMenuInflater();
            inflater.inflate(R.menu.context_menu, menu);
            return true;
        }
    
        // Called each time the action mode is shown. Always called after onCreateActionMode, but
        // may be called multiple times if the mode is invalidated.
        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return false; // Return false if nothing is done
        }
    
        // Called when the user selects a contextual menu item
        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            switch (item.getItemId()) {
                case R.id.menu_share:
                    shareCurrentItem();
                    mode.finish(); // Action picked, so close the CAB
                    return true;
                default:
                    return false;
            }
        }
    
        // Called when the user exits the action mode
        @Override
        public void onDestroyActionMode(ActionMode mode) {
            actionMode = null;
        }
    };
    

    これらのイベント コールバックは、それぞれでイベントに関連する ActionMode オブジェクトを渡すことを除いて、オプション メニューのコールバックとほぼ同じです。ActionMode API を使って、setTitle()setSubtitle()(選択されている項目数を表示するのに役立ちます)でタイトルとサブタイトルを変更するなど、CAB にさまざまな変更を行えます。

    また、上記のサンプルでは、アクション モードが破棄されるときに actionMode 変数が null に設定されます。次のステップでは、どのように初期化が行われるか、メンバー変数をアクティビティやフラグメントに保存することがどのように役立つかを説明します。

  2. startActionMode() を呼び出して、View で長押しクリックに応答するときなど、必要に応じてコンテキスト アクション モードを有効にします。

    Kotlin

    someView.setOnLongClickListener { view ->
        // Called when the user long-clicks on someView
        when (actionMode) {
            null -> {
                // Start the CAB using the ActionMode.Callback defined above
                actionMode = activity?.startActionMode(actionModeCallback)
                view.isSelected = true
                true
            }
            else -> false
        }
    }
    

    Java

    someView.setOnLongClickListener(new View.OnLongClickListener() {
        // Called when the user long-clicks on someView
        public boolean onLongClick(View view) {
            if (actionMode != null) {
                return false;
            }
    
            // Start the CAB using the ActionMode.Callback defined above
            actionMode = getActivity().startActionMode(actionModeCallback);
            view.setSelected(true);
            return true;
        }
    });
    

    startActionMode() を呼び出すと、システムは作成された ActionMode を返します。これをメンバー変数に保存すると、さまざまなイベントに応じてコンテキスト アクションバーを変更できます。上記の例では、ActionMode を使って、ActionMode インスタンスがアクティブな状態である場合に、アクション モードを開始する前にメンバーが null であるかどうかを確認して、そのインスタンスが再作成されないようにしています。

バッチ コンテキスト アクションを ListView や GridView で有効にする

ListViewGridView(または AbsListView の別の拡張)に項目のコレクションがあり、ユーザーがバッチ アクションを実行できるようにする場合は、次のことを行う必要があります。

次に例を示します。

Kotlin

val listView: ListView = getListView()
with(listView) {
    choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL
    setMultiChoiceModeListener(object : AbsListView.MultiChoiceModeListener {
        override fun onItemCheckedStateChanged(mode: ActionMode, position: Int,
                                               id: Long, checked: Boolean) {
            // Here you can do something when items are selected/de-selected,
            // such as update the title in the CAB
        }

        override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
            // Respond to clicks on the actions in the CAB
            return when (item.itemId) {
                R.id.menu_delete -> {
                    deleteSelectedItems()
                    mode.finish() // Action picked, so close the CAB
                    true
                }
                else -> false
            }
        }

        override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
            // Inflate the menu for the CAB
            val menuInflater: MenuInflater = mode.menuInflater
            menuInflater.inflate(R.menu.context, menu)
            return true
        }

        override fun onDestroyActionMode(mode: ActionMode) {
            // Here you can make any necessary updates to the activity when
            // the CAB is removed. By default, selected items are deselected/unchecked.
        }

        override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
            // Here you can perform updates to the CAB due to
            // an <code><a href="/reference/android/view/ActionMode.html#invalidate()">invalidate()</a></code> request
            return false
        }
    })
}

Java

ListView listView = getListView();
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
listView.setMultiChoiceModeListener(new MultiChoiceModeListener() {

    @Override
    public void onItemCheckedStateChanged(ActionMode mode, int position,
                                          long id, boolean checked) {
        // Here you can do something when items are selected/de-selected,
        // such as update the title in the CAB
    }

    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        // Respond to clicks on the actions in the CAB
        switch (item.getItemId()) {
            case R.id.menu_delete:
                deleteSelectedItems();
                mode.finish(); // Action picked, so close the CAB
                return true;
            default:
                return false;
        }
    }

    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        // Inflate the menu for the CAB
        MenuInflater inflater = mode.getMenuInflater();
        inflater.inflate(R.menu.context, menu);
        return true;
    }

    @Override
    public void onDestroyActionMode(ActionMode mode) {
        // Here you can make any necessary updates to the activity when
        // the CAB is removed. By default, selected items are deselected/unchecked.
    }

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        // Here you can perform updates to the CAB due to
        // an <code><a href="/reference/android/view/ActionMode.html#invalidate()">invalidate()</a></code> request
        return false;
    }
});

これで完了です。これで、ユーザーが長押しクリックで項目を選択すると、システムは onCreateActionMode() メソッドを呼び出して、指定されたアクションでコンテキスト アクションバーを表示するようになります。コンテキスト アクションバーが表示されている間は、追加の項目を選択できます。

コンテキスト アクションによって一般的なアクション項目が提供されるとき、ユーザーが長押しクリックの動作に気付かない可能性があることを考慮して、項目を選択するためのチェックボックスや同様の UI 要素を追加したい場合もあります。ユーザーがチェックボックスをオンにすると、setItemChecked() でオンにされた状態にそれぞれのリスト項目を設定して、コンテキスト アクション モードを呼び出すことができます。

ポップアップ メニューの作成

図 4. 右上のオーバーフロー ボタンに固定された Gmail アプリのポップアップ メニュー。

PopupMenu は、View に固定されたモーダル メニューです。スペースがある場合にはアンカービューの下に、スペースがない場合はビューの上に表示されます。次の場合に役立ちます。

  • 特定のコンテンツに関連するアクションにオーバーフロー スタイルのメニューを提供する(図 4 にある Gmail のメールヘッダーなど)。

    注: このメニューは、一般に選択したコンテンツに影響するアクション用であるコンテキスト メニューとは異なります。選択したコンテンツに影響するアクションには、コンテキスト アクション モードフローティング コンテキスト メニューを使います。

  • コマンド センテンスの 2 番目の部分を提供する(別の「追加」オプションを含むポップアップ メニューを生成する「追加」とマークされたボタンなど)。
  • 固定選択を保持しない Spinner のようなプルダウンを提供する。

注: PopupMenu は、API レベル 11 以降で使用できます。

XML でメニューを定義する場合の、ポップアップ メニューの表示方法について、次に示します。

  1. コンストラクタを使って PopupMenu をインスタンス化します。これにより、メニューを固定する必要のある、現在のアプリの ContextView が取得されます。
  2. MenuInflater を使って、PopupMenu.getMenu() によって返される Menu オブジェクトにメニュー リソースをインフレートします。
  3. PopupMenu.show() を呼び出します。

ポップアップ メニューを表示する android:onClick 属性を含むボタンの例を以下に示します。

<ImageButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_overflow_holo_dark"
    android:contentDescription="@string/descr_overflow_button"
    android:onClick="showPopup" />

アクティビティでは、次のようにポップアップ メニューが表示されます。

Kotlin

fun showPopup(v: View) {
    val popup = PopupMenu(this, v)
    val inflater: MenuInflater = popup.menuInflater
    inflater.inflate(R.menu.actions, popup.menu)
    popup.show()
}

Java

public void showPopup(View v) {
    PopupMenu popup = new PopupMenu(this, v);
    MenuInflater inflater = popup.getMenuInflater();
    inflater.inflate(R.menu.actions, popup.getMenu());
    popup.show();
}

API レベル 14 以降では、PopupMenu.inflate() でメニューをインフレートする 2 行を組み合わせることができます。

ユーザーが項目を選択するか、メニューの領域外をタップすると、メニューが閉じます。PopupMenu.OnDismissListener を使って dismiss イベントをリッスンできます。

クリック イベントの処理

ユーザーがメニュー項目を選択したときにアクションを実行するには、PopupMenu.OnMenuItemClickListener インターフェースを実装し、setOnMenuItemclickListener() を呼び出して PopupMenu で登録する必要があります。ユーザーが項目を選択すると、インターフェースで onMenuItemClick() コールバックが呼び出されます。

次に例を示します。

Kotlin

fun showMenu(v: View) {
    PopupMenu(this, v).apply {
        // MainActivity implements OnMenuItemClickListener
        setOnMenuItemClickListener(this@MainActivity)
        inflate(R.menu.actions)
        show()
    }
}

override fun onMenuItemClick(item: MenuItem): Boolean {
    return when (item.itemId) {
        R.id.archive -> {
            archive(item)
            true
        }
        R.id.delete -> {
            delete(item)
            true
        }
        else -> false
    }
}

Java

public void showMenu(View v) {
    PopupMenu popup = new PopupMenu(this, v);

    // This activity implements OnMenuItemClickListener
    popup.setOnMenuItemClickListener(this);
    popup.inflate(R.menu.actions);
    popup.show();
}

@Override
public boolean onMenuItemClick(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.archive:
            archive(item);
            return true;
        case R.id.delete:
            delete(item);
            return true;
        default:
            return false;
    }
}

メニュー グループの作成

メニュー グループは、特定の特徴を共有するメニュー項目のコレクションです。グループを使って次のことを実行できます。

メニュー リソースの <group> 要素内で <item> 要素をネストするか、add() メソッドでグループ ID を指定して、グループを作成できます。

グループを含むメニュー リソースの一例を次に示します。

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/menu_save"
          android:icon="@drawable/menu_save"
          android:title="@string/menu_save" />
    <!-- menu group -->
    <group android:id="@+id/group_delete">
        <item android:id="@+id/menu_archive"
              android:title="@string/menu_archive" />
        <item android:id="@+id/menu_delete"
              android:title="@string/menu_delete" />
    </group>
</menu>

グループ内の項目は、最初の項目と同じレベルで表示されます(メニューの 3 つの項目すべてが兄弟)。ただし、グループ ID を参照するか、上記のメソッドを使って、グループ内の 2 つの項目の特徴を変更できます。システムが、グループ化された項目を分けることもありません。たとえば、各項目に android:showAsAction="ifRoom" を宣言する場合、その両方がアクションバーまたはアクション オーバーフローに表示されます。

オンにできるメニュー項目の使用

図 5. オンにできるアイテムを含むサブメニューのスクリーンショット

メニューは、オプションのオンとオフを切り替えるインターフェースとして役立ちます。スタンドアロンのオプションにはチェックボックスを、相互に排他的なオプションのグループにはラジオボタンを使います。図 5 に、ラジオボタン付きのオンにできる項目を含むサブメニューを示します。

注: アイコン メニューのメニュー項目(オプション メニューから)ではチェックボックスやラジオボタンを表示できません。アイコン メニューの項目をオンにできるようにする場合、状態が変わるごとにアイコンやテキストを入れ替えて、オンにされた状態を手動で示す必要があります。

個々のメニュー項目には <item> 要素の android:checkable 属性を、グループ全体には <group> 要素の android:checkableBehavior 属性を使って、オンにする際の動作を定義できます。たとえば、このメニュー グループのすべての項目はラジオボタンでオンにできます。

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item android:id="@+id/red"
              android:title="@string/red" />
        <item android:id="@+id/blue"
              android:title="@string/blue" />
    </group>
</menu>

android:checkableBehavior 属性には、次のいずれかを指定できます。

single
グループから 1 つの項目のみをオンにできる(ラジオボタン)
all
すべての項目をオンにできる(チェックボックス)
none
どの項目もオンにできない

<item> 要素の android:checked 属性を使うと、項目をデフォルトでオンになった状態にすることができ、setChecked() メソッドを使うと、その状態をコード内で変更できます。

オンにできる項目が選択されると、システムは onOptionsItemSelected() などの、それぞれの item-selected コールバック メソッドを呼び出します。チェックボックスやラジオボタンによって自動的にその状態が変わることはないため、ここでチェックボックスの状態を設定する必要があります。isChecked() で、項目の現在の状態(ユーザーが選択する前の状態)を照会できます。その後、setChecked() でオンにされた状態を設定します。次に例を示します。

Kotlin

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    return when (item.itemId) {
        R.id.vibrate, R.id.dont_vibrate -> {
            item.isChecked = !item.isChecked
            true
        }
        else -> super.onOptionsItemSelected(item)
    }
}

Java

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.vibrate:
        case R.id.dont_vibrate:
            if (item.isChecked()) item.setChecked(false);
            else item.setChecked(true);
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

この方法でオンにされた状態を設定しない場合には、項目(チェックボックスまたはラジオボタン)の表示状態はユーザーがオンにしたときに変更されません。状態を設定すると、アクティビティは項目のオンにされた状態を維持して、ユーザーが後でメニューを開いたときに、開発者が設定したオンにされた状態が表示されるようになります。

注: オンにできるメニュー項目は、セッション単位ベースのみでの使用を意図したものであり、アプリが破棄された後は、保存されません。ユーザー用に保存するアプリの設定がある場合は、共有設定を使ってデータを保存してください。

インテントに基づくメニュー項目の追加

自分のアプリのアクティビティであるか、別のアプリのアクティビティであるかにかかわらず、Intent を使ってメニュー項目でアクティビティが起動されるようにしたい場合もあります。使用するインテントがわかっていて、インテントを開始する必要のある特定のメニュー項目がある場合は、項目が選択されたときに呼び出される適切なコールバック メソッド(onOptionsItemSelected() コールバックなど)中に、startActivity() でインテントを実行できます。

ただし、ユーザーのデバイスにインテントを処理するアプリが含まれているかどうかが不明な場合、そのインテントを呼び出すメニュー項目を追加すると、そのインテントによってアクティビティが解決されない可能性があり、メニュー項目が機能しなくなることがあります。この問題を解決するために、Android では、インテントを処理するデバイスで Android によってアクティビティが検出されるときに、メニュー項目がメニューに動的に追加されるようにします。

インテントを受け入れる使用可能なアクティビティに基づいてメニュー項目を追加するには:

  1. CATEGORY_ALTERNATIVE カテゴリや CATEGORY_SELECTED_ALTERNATIVE カテゴリでインテントを定義します。その他の要件も必要です。
  2. Menu.addIntentOptions() を呼び出します。その際、Android によって、インテントを実行できるアプリが検索され、メニューにそのアプリが追加されます。

インテントに対応するアプリがインストールされていない場合、メニュー項目は追加されません。

注: CATEGORY_SELECTED_ALTERNATIVE は、現在画面で選択されている要素の処理に使われます。これは、onCreateContextMenu() でメニューを作成するときにのみ使用する必要があります。

次に例を示します。

Kotlin

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    super.onCreateOptionsMenu(menu)

    // Create an Intent that describes the requirements to fulfill, to be included
    // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE.
    val intent = Intent(null, dataUri).apply {
        addCategory(Intent.CATEGORY_ALTERNATIVE)
    }

    // Search and populate the menu with acceptable offering applications.
    menu.addIntentOptions(
            R.id.intent_group,  // Menu group to which new items will be added
            0,                  // Unique item ID (none)
            0,                  // Order for the items (none)
            this.componentName, // The current activity name
            null,               // Specific items to place first (none)
            intent,             // Intent created above that describes our requirements
            0,                  // Additional flags to control items (none)
            null)               // Array of MenuItems that correlate to specific items (none)

    return true
}

Java

@Override
public boolean onCreateOptionsMenu(Menu menu){
    super.onCreateOptionsMenu(menu);

    // Create an Intent that describes the requirements to fulfill, to be included
    // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE.
    Intent intent = new Intent(null, dataUri);
    intent.addCategory(Intent.CATEGORY_ALTERNATIVE);

    // Search and populate the menu with acceptable offering applications.
    menu.addIntentOptions(
         R.id.intent_group,  // Menu group to which new items will be added
         0,      // Unique item ID (none)
         0,      // Order for the items (none)
         this.getComponentName(),   // The current activity name
         null,   // Specific items to place first (none)
         intent, // Intent created above that describes our requirements
         0,      // Additional flags to control items (none)
         null);  // Array of MenuItems that correlate to specific items (none)

    return true;
}

定義されたインテントに一致するインテント フィルタが提供されていることを見つけた各アクティビティに対して、インテント フィルタの android:label の値をメニュー項目のタイトルとして、アプリのアイコンをメニュー項目のアイコンとして使って、メニュー項目が追加されます。addIntentOptions() メソッドによって、追加されたメニュー項目数が返されます。

注: addIntentOptions() を呼び出すとき、最初の引数で指定されたメニュー グループによって、すべてのメニュー項目がオーバーライドされます。

アクティビティを他のメニューに追加できるようにする

他のアプリにアクティビティのサービスを提供して、アプリを他のメニューに含めることができるようにすることもできます(前述の役割を逆にします)。

他のアプリのメニューに含まれるようにするには、通常どおりインテント フィルタを定義する必要がありますが、インテント フィルタのカテゴリに、CATEGORY_ALTERNATIVE 値や CATEGORY_SELECTED_ALTERNATIVE 値を含めるようにしてください。次に例を示します。

<intent-filter label="@string/resize_image">
    ...
    <category android:name="android.intent.category.ALTERNATIVE" />
    <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
    ...
</intent-filter>

インテント フィルタの記述について詳しくは、インテントとインテント フィルタのドキュメントをご覧ください。