添加菜单

试试 Compose 方式
Jetpack Compose 是 Android 推荐的 UI 工具包。了解如何在 Compose 中添加组件。

菜单是许多类型应用中常见的用户界面组件。为了提供熟悉且一致的用户体验,请使用 Menu API 在您的 Activity 中呈现用户操作和其他选项。

An image showing an example of overflow menu
图 1. 通过图标轻触触发并在溢出菜单图标下方显示的菜单。

本文档介绍如何在所有版本的 Android 上创建三种基本类型的菜单或操作呈现方式

选项菜单和应用栏
选项菜单是 Activity 的菜单项的主要集合。您可以在其中放置对应用具有全局影响的操作,例如“搜索”、“撰写电子邮件”和“设置”。

请参阅创建选项菜单部分。

上下文菜单和上下文操作模式
上下文菜单是一个浮动菜单,当用户在元素上执行触碰并长按时出现。它提供会影响所选内容或上下文框架的操作。

上下文操作模式(请参阅上下文操作模式)在屏幕顶部的栏中显示影响所选内容的动作项,并允许用户选择多个项。

请参阅创建上下文菜单部分。

弹出式菜单
弹出式菜单会显示一个垂直列表,并固定到调用该菜单的视图上。它适用于提供与特定内容相关的溢出操作,或为命令的第二部分提供选项。弹出式菜单中的操作不会直接影响相应内容(上下文操作就是为此目的而设计的)。相反,弹出式菜单适用于与您的 Activity 中的内容区域相关的扩展操作。

请参阅创建弹出式菜单部分。

在 XML 中定义菜单

对于所有菜单类型,Android 都提供标准的 XML 格式来定义菜单项。与其在 Activity 的代码中构建菜单,不如在 XML 菜单资源中定义菜单及其所有项。然后,您可以在 Activity 或 Fragment 中膨胀(即加载为 Menu 对象)该菜单资源。

使用菜单资源是最佳实践,原因如下

  • 在 XML 中更易于可视化菜单结构。
  • 它将菜单内容与应用的行为代码分离。
  • 通过利用应用资源框架,您可以针对不同的平台版本、屏幕尺寸和其他配置创建备用菜单配置。

要定义菜单,请在项目的 res/menu/ 目录中创建 XML 文件,并使用以下元素构建菜单

<menu>
定义 Menu,它是菜单项的容器。<menu> 元素必须是文件的根节点,并且可以包含一个或多个 <item><group> 元素。
<item>
创建 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>

要在您的 Activity 中使用菜单,请膨胀菜单资源,使用 MenuInflater.inflate() 将 XML 资源转换为可编程对象。以下部分展示了如何为每种菜单类型膨胀菜单。

创建选项菜单

选项菜单,如图 1 所示,是您包含与当前 Activity 上下文相关的操作和其他选项的地方,例如“搜索”、“撰写电子邮件”和“设置”。

An image showing the app bar for the Google Sheets app
图 2. Google 表格应用,显示了多个按钮,包括操作溢出按钮。

您可以从您的 Activity 子类或 Fragment 子类声明选项菜单项。如果您的 Activity 和 Fragment 都声明了选项菜单项,这些项会合并到 UI 中。Activity 的项首先出现,然后是每个 Fragment 的项,顺序取决于 Fragment 添加到 Activity 的顺序。如有必要,您可以使用需要移动的每个 <item> 中的 android:orderInCategory 属性重新排序菜单项。

要为 Activity 指定选项菜单,请覆盖 onCreateOptionsMenu()。Fragment 提供自己的 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 修改其属性。

处理点击事件

当用户从选项菜单(包括应用栏中的操作项)中选择一项时,系统会调用您 Activity 的 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。

如果您的 Activity 包含 Fragment,系统会先调用 Activity 的 onOptionsItemSelected(),然后按 Fragment 添加的顺序调用每个 Fragment 的该方法,直到某个 Fragment 返回 true 或所有 Fragment 都被调用为止。

在运行时更改菜单项

系统调用 onCreateOptionsMenu() 后,会保留您填充的 Menu 实例,除非菜单失效,否则不会再次调用 onCreateOptionsMenu()。但是,只能使用 onCreateOptionsMenu() 来创建初始菜单状态,而不能在 Activity 生命周期期间进行更改。

如果您想根据 Activity 生命周期期间发生的事件修改选项菜单,可以在 onPrepareOptionsMenu() 方法中进行。此方法会向您传递当前存在的 Menu 对象,以便您可以修改它,例如添加、移除或禁用项。Fragment 也提供 onPrepareOptionsMenu() 回调。

当菜单项呈现在应用栏中时,选项菜单被视为始终打开。当发生事件并且您要执行菜单更新时,请调用 invalidateOptionsMenu() 请求系统调用 onPrepareOptionsMenu()

创建上下文菜单

An image showing a floating context menu
图 3. 浮动上下文菜单。

上下文菜单提供会影响 UI 中特定项或上下文框架的操作。您可以为任何视图提供上下文菜单,但它们最常用于 RecylerView 或其他视图集合中的项,用户可以在其中对每个项执行直接操作。

提供上下文操作有两种方式

  • 浮动上下文菜单中。当用户在声明支持上下文菜单的视图上执行触碰并长按时,菜单会显示为浮动菜单项列表,类似于对话框。用户每次只能对一个项执行上下文操作。
  • 上下文操作模式中。此模式是 ActionMode 的系统实现,在屏幕顶部显示一个上下文操作栏或 CAB,其中包含影响所选项目(或多个项目)的动作项。启用此模式后,如果您的应用支持,用户可以同时对多个项目执行操作。

注意:上下文菜单不支持项快捷方式和项图标。

创建浮动上下文菜单

要提供浮动上下文菜单,请执行以下操作

  1. 通过调用 registerForContextMenu() 并向其传递 View,注册与上下文菜单关联的 View

    如果您的 Activity 使用 RecyclerView,并且您希望每个项都提供相同的上下文菜单,请通过将 RecyclerView 传递给 registerForContextMenu() 来注册所有项以获取上下文菜单。

  2. 在您的 ActivityFragment 中实现 onCreateContextMenu() 方法。

    当注册的视图收到触碰并长按事件时,系统会调用您的 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 对象。如果您的 Activity 有多个视图,每个视图都提供不同的上下文菜单,您可以使用这些参数来确定要膨胀哪个上下文菜单。

  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 属性将该 ID 分配给每个菜单项,如在 XML 中定义菜单所示。

    如果您成功处理了菜单项,请返回 true。如果您未处理菜单项,请将菜单项传递给超类实现。如果您的 Activity 包含 Fragment,Activity 会首先接收此回调。通过在未处理时调用超类,系统会按每个 Fragment 添加的顺序将事件传递给每个 Fragment 中相应的回调方法,直到返回 truefalse 为止。Activityandroid.app.Fragment 的默认实现会返回 false,因此在未处理时始终调用超类。

使用上下文操作模式

上下文操作模式是 ActionMode 的系统实现,其目的是将用户交互集中于执行上下文操作。当用户通过选择一项来启用此模式时,屏幕顶部会出现一个上下文操作栏或 CAB,用于显示用户可以对所选项执行的操作。启用此模式后,如果您的应用支持,用户可以选择多个项,并且可以取消选择项并继续在 Activity 中导航。当用户取消选择所有项、点按返回按钮或点按操作栏左侧的完成操作时,操作模式将被禁用,上下文操作栏会消失。

对于提供上下文操作的视图,您通常在发生以下两个事件中的一个或两个时调用上下文操作模式

  • 用户在视图上执行触碰并长按。
  • 用户在视图中选择复选框或类似的界面组件。

您的应用如何调用上下文操作模式以及如何定义每个操作的行为取决于您的设计。有两种设计

  • 针对单个任意视图的上下文操作。
  • 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 对 CAB 进行各种更改,例如使用 setTitle()setSubtitle() 修改标题和副标题,这对于指示选择了多少项非常有用。

    前面的示例在操作模式被销毁时将 actionMode 变量设置为 null。在下一步中,查看它如何初始化,以及在您的 Activity 或 Fragment 中保存成员变量如何有用。

  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。通过将此对象保存在成员变量中,您可以根据其他事件对上下文操作栏进行各种更改。在前面的示例中,通过在启动操作模式之前检查成员是否为 null,使用 ActionMode 来确保如果 ActionMode 实例已处于活动状态,则不会重新创建它。

创建弹出式菜单

An image showing a popup menu in the Gmail app, anchored to the overflow button at the top-right.
图 4. Gmail 应用中的弹出式菜单,固定到右上角的溢出按钮。

一个 PopupMenu 是固定到 View 的模态菜单。如果有空间,它会出现在锚定视图下方,否则出现在上方。它适用于以下情况

  • 特定内容相关的操作提供溢出样式菜单,例如图 4 中所示的 Gmail 电子邮件标头。
  • 提供命令语句的第二部分,例如标记为添加的按钮,该按钮会产生一个包含不同添加选项的弹出式菜单。
  • 提供类似于 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" />

然后 Activity 可以像这样显示弹出式菜单

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;
    }
}

创建菜单组

菜单组是共享某些特征的菜单项的集合。使用组,您可以执行以下操作

您可以通过在菜单资源的 <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>

组中的项与第一个项出现在同一级别——菜单中的所有三个项都是同级。但是,您可以通过引用组 ID 并使用上述方法修改组中两个项的特性。系统也绝不会分隔分组的项。例如,如果您为每个项声明 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
组中只能选中一个项,结果是单选按钮。
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);
    }
}

如果您不以这种方式设置选中状态,那么当用户选择复选框或单选按钮时,其可见状态不会改变。设置状态后,Activity 会保留项的选中状态,以便用户稍后打开菜单时,您设置的选中状态会可见。

基于 Intent 添加菜单项

有时您希望菜单项使用 Intent 启动一个 Activity,无论是您应用中的 Activity 还是其他应用中的 Activity。当您知道要使用的 Intent 并且有一个启动该 Intent 的特定菜单项时,您可以在相应的项选中回调方法(例如 onOptionsItemSelected() 回调)期间使用 startActivity() 执行该 Intent。

但是,如果您不确定用户的设备上是否有处理该 Intent 的应用,那么添加一个调用该 Intent 的菜单项可能会导致菜单项无法正常工作,因为该 Intent 可能无法解析为 Activity。为了解决这个问题,当 Android 在设备上找到处理您的 Intent 的 Activity 时,Android 允许您动态地向菜单添加菜单项。

要根据接受 Intent 的可用 Activity 添加菜单项,请执行以下操作

  1. 定义一个 Intent,其类别为 CATEGORY_ALTERNATIVECATEGORY_SELECTED_ALTERNATIVE,或两者兼有,再加上任何其他要求。
  2. 调用 Menu.addIntentOptions()。然后 Android 会搜索任何可以执行该 Intent 的应用,并将其添加到您的菜单中。

如果没有安装满足该 Intent 的应用,则不会添加任何菜单项。

示例如下

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;
}

对于找到的每个提供与定义的 Intent 匹配的 Intent 过滤器的 Activity,都会添加一个菜单项,使用 Intent 过滤器的 android:label 中的值作为菜单项标题,并使用应用图标作为菜单项图标。addIntentOptions() 方法返回添加的菜单项数量。

让您的 Activity 被添加到其他菜单中

您可以将您的 Activity 服务提供给其他应用,以便您的应用可以包含在其他应用的菜单中——反转了前面描述的角色。

要包含在其他应用菜单中,像往常一样定义 Intent 过滤器,但将 CATEGORY_ALTERNATIVECATEGORY_SELECTED_ALTERNATIVE 值(或两者)包含在 Intent 过滤器类别中。示例如下

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

如需详细了解如何编写 Intent 过滤器,请参阅Intent 和 Intent 过滤器