メニューを追加する

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
このアイテムがアプリバーのアクション アイテムとして、いつ、どのように表示される必要があるのかを指定します。

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

項目にサブメニューを追加するには、各メニューで、<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 に示すように、オプション メニューには、「検索」、「メールの作成」、「設定」などの、現在のアクティビティ コンテキストに関連するアクションとその他のオプションを配置します。

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() を呼び出します。これにより、メニュー項目に対して一意の 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 が返されるまで、またはすべてのフラグメントが呼び出されるまで、フラグメントが追加された順序で、各フラグメントに対してこのメソッドを呼び出します。

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

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

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

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

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

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

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

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

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

注: コンテキスト メニューは、アイテムのショートカットとアイテムのアイコンをサポートしていません。

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

フローティング コンテキスト メニューを提供する手順は次のとおりです。

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

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

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

コンテキスト アクションを提供するビューでは、通常は次の 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 を使って、ActionMode インスタンスがアクティブな状態である場合に、アクション モードを開始する前にメンバーが null であるかどうかを確認して、そのインスタンスが再作成されないようにしています。

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

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

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

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

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

  1. コンストラクタを使って PopupMenu をインスタンス化します。これにより、メニューを固定する必要のある、現在のアプリの ContextView が取得されます。
  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 を使って 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;
    }
}

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

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

  • 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() などの、それぞれの 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 によって、インテントを実行できるアプリが検索され、メニューにそのアプリが追加されます。

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

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

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>

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