The Android Developer Challenge is back! Submit your idea before December 2.

メニュー

メニューは、さまざまなタイプのアプリで共通するユーザー インターフェースです。使い慣れた一貫したユーザー エクスペリエンスを提供するには、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 Play Movies アプリ

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() を使ってメニュー項目を追加し、MenuItem API でそのプロパティを修正するために、findItem() で項目を取得することもできます。

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

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

ユーザーがオプション メニューから項目を選択すると(アプリバーのアクション項目を含む)、アクティビティの onOptionsItemSelected() メソッドが呼び出されます。このメソッドでは、選択された MenuItem が渡されます。getItemId() を呼び出して項目を識別できます。これにより、メニュー項目に対して一意の ID(メニュー リソースで android: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() のスーパークラスの実装を呼び出す必要があります(デフォルトの実装では fause が返されます)。

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

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

ヒント: アプリに複数のアクティビティが含まれていて、その一部で同じオプション メニューが提供されている場合、onCreateOptionsMenu()onOptionsItemSelected() メソッドのみを実装するアクティビティを作成することを検討します。その後、同じオプション メニューを共有する必要のある各アクティビティのこのクラスを拡張します。この方法で、メニューの動作を継承するメニュー アクションとそれぞれの子孫クラスを処理するためのコードを 1 セットで管理できます。子孫アクティビティの 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 を照会します。XML でのメニューの定義のセクションで説明されているように、android:id 属性を使って XML で各メニュー項目を割り当てる必要があります。

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

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

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

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

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

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

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

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

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

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

ユーザーが特定のビューを選択するときにのみ、コンテキスト アクション モードを呼び出すには、次のことを行う必要があります。

  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 にさまざまな変更を行うことができます。

    また、上記のサンプルでは、アクション モードが破棄されるときに mActionMode 変数が 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 アプリのポップアップ メニュー

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

  • 特定のコンテンツに関連するアクションにオーバーフロー スタイルのメニューを提供する(図 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_ALTERNATIVECATEGORY_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_ALTERNATIVECATEGORY_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>

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