メニューを追加する

Compose を試す
Jetpack Compose は Android の推奨 UI ツールキットです。Compose でコンポーネントを追加する方法を学習します。

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

オーバーフロー メニューの例を示す画像
図 1.アイコンのタップによってトリガーされるメニュー。オーバーフロー メニュー アイコンの下に表示されます。

このドキュメントでは、すべてのバージョンの 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"
          app: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
このアイテムがアプリバーにアクション アイテムとして表示されるタイミングと方法の仕様。

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

どのメニューのアイテムにもサブメニューを追加するには、<menu> 要素を <item> の子として追加します。サブメニューは、PC アプリのメニューバーのアイテム(FileEditView など)のように、トピックごとに整理できる機能がアプリに数多くある場合に便利です。次の例をご覧ください。

<?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 のようなオプション メニューには、現在のアクティビティ コンテキストに関連するアクションやその他のオプション([検索]、[メールを作成]、[設定] など)が含まれます。

Google スプレッドシート アプリのアプリバーを示す画像
図 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 でプロパティを変更することもできます。

クリック イベントを処理する

ユーザーがアプリバーのアクション アイテムなどのオプション メニューからアイテムを選択すると、アクティビティの onOptionsItemSelected() メソッドが呼び出されます。このメソッドでは、選択された MenuItem が渡されます。getItemId() を呼び出すことで、メニュー リソースの android:id 属性で定義されたメニュー アイテムの一意の ID を返すか、add() メソッドに整数を指定して、メニュー アイテムの一意の ID を返します。この 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 を返すか、すべてのフラグメントが呼び出されるまで、フラグメントが追加された順に各フラグメントが追加されます。

実行時にメニュー項目を変更する

システムが onCreateOptionsMenu() を呼び出した後は、ユーザーが入力した Menu のインスタンスを保持し、メニューが無効になっていない限り、onCreateOptionsMenu() を再度呼び出すことはありません。ただし、onCreateOptionsMenu() は初期のメニュー状態を作成する目的にのみ使用され、アクティビティのライフサイクル中に変更を行わないようにします。

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

オプション メニューは、メニュー項目がアプリバーに表示されているときに常に開いていると見なされます。イベント発生時にメニューを更新する場合は、invalidateOptionsMenu() を呼び出して、onPrepareOptionsMenu() を呼び出すようシステムにリクエストします。

コンテキスト メニューを作成する

フローティング コンテキスト メニューを示す画像
図 3. フローティング コンテキスト メニュー

コンテキスト メニューには、UI の特定のアイテムまたはコンテキスト フレームに影響するアクションが表示されます。どのビューにもコンテキスト メニューを提供できますが、多くの場合は、RecylerView 内のアイテム、またはユーザーが各アイテムに直接アクションを実行できる他のビュー コレクション内のアイテムに使用されます。

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

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

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

フローティング コンテキスト メニューを表示する方法は次のとおりです。

  1. registerForContextMenu() を呼び出して View を渡すことで、コンテキスト メニューが関連付けられている View を登録します。

    アクティビティで RecyclerView を使用していて、各アイテムに同じコンテキスト メニューを提供する場合は、RecyclerViewregisterForContextMenu() に渡してコンテキスト メニューにすべてのアイテムを登録します。

  2. Activity または FragmentonCreateContextMenu() メソッドを実装します。

    登録されたビューが長押しイベントを受信すると、システムによって 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 を返します。メニュー項目を処理しない場合は、メニュー項目をスーパークラスの実装に渡します。アクティビティにフラグメントが含まれている場合、アクティビティは最初にこのコールバックを受け取ります。未処理のときにスーパークラスを呼び出すと、true または false が返されるまで、各フラグメントが追加された順に、各フラグメントのそれぞれのコールバック メソッドにイベントが 1 つずつ渡されます。Activityandroid.app.Fragment のデフォルトの実装は false を返すため、未処理の場合は常にスーパークラスを呼び出します。

コンテキスト アクション モードを使用する

コンテキスト アクション モードは、コンテキスト アクションの実行に対するユーザーの操作に重点を置いた ActionMode のシステム実装です。ユーザーがアイテムを選択してこのモードを有効にすると、画面上部にコンテキスト アクションバーが表示され、選択したアイテムに対してユーザーが実行できるアクションが表示されます。このモードが有効なとき、ユーザーは複数のアイテムを選択できます(アプリがサポートしている場合)。また、アイテムの選択を解除してアクティビティ内で移動を続けることができます。ユーザーがすべてのアイテムの選択を解除するか、[戻る] ボタンをタップするか、バーの左側の [完了] アクションをタップすると、アクション モードは無効になり、コンテキスト アクションバーは非表示になります。

コンテキスト アクションを提供するビューの場合、通常は次の 2 つのイベントのいずれかまたは両方が発生したときに、コンテキスト アクション モードが呼び出されます。

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

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

  • 個別の任意のビューでのコンテキスト アクション用。
  • RecyclerView 内のアイテムのグループに対してコンテキストに応じたバッチ アクションを実行する場合、ユーザーは複数のアイテムを選択して、それらすべてに対してアクションを実行できます。

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

個々のビューでコンテキスト アクション モードを有効にする

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

  1. 次の例に示すように、ActionMode.Callback インターフェースを実装します。そのコールバック メソッドでは、コンテキスト アクションバーのアクションの指定、アクション アイテムのクリック イベントへの応答、アクション モードのその他のライフサイクル イベントの処理を行うことができます。

    Kotlin

        private val actionModeCallback = object : ActionMode.Callback {
            // Called when the action mode is created. startActionMode() is 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, and might 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() is 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, and might 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() を呼び出します。

    Kotlin

        someView.setOnLongClickListener { view ->
            // Called when the user performs a touch & hold on someView.
            when (actionMode) {
                null -> {
                    // Start the CAB using the ActionMode.Callback defined earlier.
                    actionMode = activity?.startActionMode(actionModeCallback)
                    view.isSelected = true
                    true
                }
                else -> false
            }
        }
        

    Java

        someView.setOnLongClickListener(new View.OnLongClickListener() {
            // Called when the user performs a touch & hold on someView.
            public boolean onLongClick(View view) {
                if (actionMode != null) {
                    return false;
                }
    
                // Start the CAB using the ActionMode.Callback defined earlier.
                actionMode = getActivity().startActionMode(actionModeCallback);
                view.setSelected(true);
                return true;
            }
        });
        

    startActionMode() を呼び出すと、作成された ActionMode が返されます。これをメンバー変数に保存すると、他のイベントに応じてコンテキスト アクションバーを変更できます。 上記のサンプルでは、ActionMode を使用して、アクション モードを開始する前にメンバーが null かどうかをチェックすることで、ActionMode インスタンスがすでにアクティブな場合でも再作成されないようにしています。

ポップアップ メニューを作成する

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

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

  • 図 4 に示すように、特定のコンテンツに関連するアクション(Gmail のメールのヘッダーなど)にオーバーフロー スタイルのメニューを提供する。
  • コマンド センテンスの 2 番目の部分を提供する。たとえば、[Add] とマークされたボタンで、さまざまな [Add] オプションを含むポップアップ メニューを生成する。
  • 永続的な選択を保持しない Spinner と同様のメニューを提供する。

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

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

例として、ポップアップ メニューを表示するボタンを次に示します。

<ImageButton
    android:id="@+id/dropdown_menu"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:contentDescription="@string/descr_overflow_button"
    android:src="@drawable/arrow_drop_down" />

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

Kotlin

findViewById<ImageButton>(R.id.dropdown_menu).setOnClickListener {
    val popup = PopupMenu(this, it)
    val inflater: MenuInflater = popup.menuInflater
    inflater.inflate(R.menu.actions, popup.menu)
    popup.show()
}

Java

findViewById(R.id.dropdown_menu).setOnClickListener(v -> {
    PopupMenu popup = new PopupMenu(this, v);
    popup.getMenuInflater().inflate(R.menu.actions, popup.getMenu());
    popup.show();
});

ユーザーがアイテムを選択するか、メニュー領域の外側をタップすると、メニューが閉じます。非表示イベントをリッスンするには、PopupMenu.OnDismissListener を使用します。

クリック イベントを処理する

ユーザーがメニュー項目を選択したときにアクションを実行するには、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;
    }
}

メニュー グループを作成する

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

  • setGroupVisible() を使用して、すべてのアイテムの表示と非表示を切り替えます。
  • setGroupEnabled() を使用してすべてのアイテムを有効または無効にします。
  • setGroupCheckable() を使用して、すべてのアイテムをオンにできるかどうかを指定します。

グループを作成するには、メニュー リソースの <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() など)が呼び出されます。チェックボックスやラジオボタンでは状態が自動的に変更されないため、ここでチェックボックスの状態を設定します。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_ALTERNATIVECATEGORY_SELECTED_ALTERNATIVE、またはその両方でインテントを定義し、その他の要件も定義します。
  2. Menu.addIntentOptions() を呼び出します。Android はインテントを実行できるアプリを検索し、メニューに追加します。

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

これを次の例に示します。

Kotlin

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

    // Create an Intent that describes the requirements to fulfill, to be
    // included in the 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 apps.
    menu.addIntentOptions(
            R.id.intent_group,  // Menu group to which new items are 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 the 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 the 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 apps.
    menu.addIntentOptions(
         R.id.intent_group,         // Menu group to which new items are 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 the requirements.
         0,                         // Additional flags to control items (none).
         null);                     // Array of MenuItems that correlate to specific items (none).

    return true;
}

定義されたインテントに一致するインテント フィルタを提供するアクティビティごとに、メニュー項目が追加されます。その際、インテント フィルタの android:label の値がメニュー項目のタイトルとして、アプリアイコンがメニュー項目のアイコンとして使用されます。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>

インテント フィルタの作成方法については、インテントとインテント フィルタをご覧ください。