添加菜单

试用 Compose 方式
Jetpack Compose 是推荐在 Android 设备上使用的界面工具包。了解如何在 Compose 中添加组件。

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

显示溢出菜单示例的图片
图 1.由点按图标触发的菜单,显示在溢出菜单图标下方。

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

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

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

上下文菜单和关联操作模式
上下文菜单是用户轻触并按住某个元素时出现的浮动菜单。它提供的操作会影响所选内容或上下文框架。

关联操作模式会在屏幕顶部的栏中显示影响所选内容的操作项,并允许用户选择多项。

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

弹出式菜单
弹出式菜单会显示一系列垂直项,这些项会锚定到调用该菜单的视图中。它特别适用于提供与特定内容相关的大量操作,或为命令的第二部分提供选项。弹出式菜单中的操作不会直接影响相应内容,关联操作会起到作用。相反,弹出菜单适用于与您 activity 中的内容区域相关的扩展操作。

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

使用 XML 定义菜单

对于所有菜单类型,Android 都提供标准的 XML 格式来定义菜单项。您可以在 XML 菜单资源中定义菜单及其所有菜单项,而不是在 activity 的代码中构建菜单。然后,您可以在 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 应用菜单栏中的菜单项(如文件编辑查看),子菜单非常有用。请参阅以下示例:

<?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 上下文相关的操作和其他选项,如“搜索”“撰写电子邮件”和“设置”。

一张图片,显示 Google 表格应用的应用栏
图 2. Google 表格应用,显示了包括操作溢出菜单按钮在内的多个按钮。

您可以通过 Activity 子类或 Fragment 子类为选项菜单声明菜单项。如果 activity 和 fragment 都为选项菜单声明项,这些项会在界面中合并。系统会按照向 activity 添加 fragment 的顺序依次显示 activity 的项,后跟每个 fragment 的项。如有必要,您可以使用需要移动的每个 <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()

创建上下文菜单

显示悬浮上下文菜单的图片
图 3. 悬浮上下文菜单。

上下文菜单提供的操作会影响界面中的特定项或上下文框架。您可以为任何视图提供上下文菜单,但这些菜单通常用于 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 可让您通过菜单资源膨胀上下文菜单。回调方法参数包括用户选择的 ViewContextMenu.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,您可以使用 android:id 属性将该 ID 分配给 XML 中的每个菜单项,如在 XML 中定义菜单中所述。

    成功处理菜单项后,系统会返回 true。如果未处理菜单项,请将菜单项传递给父类实现。如果 Activity 包含 Fragment,则 Activity 会先收到此回调。通过在未处理的情况下调用父类,系统会按照每个 Fragment 的添加顺序将事件逐一传递给每个 Fragment 中相应的回调方法,直到返回 truefalseActivityandroid.app.Fragment 的默认实现会返回 false,因此请始终在未处理的情况下调用父类。

使用关联操作模式

关联操作模式是 ActionMode 的一种系统实现,它将用户互动的重点放在执行关联操作上。当用户通过选择菜单项启用此模式时,屏幕顶部会显示一个关联操作栏,其中会显示用户可以对所选项目执行的操作。启用此模式后,用户可以选择多个项(如果应用支持),并且可以取消选择项并继续在 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。通过将其保存在成员变量中,您可以更改上下文操作栏以响应其他事件。在上述示例中,ActionMode 用于在启动操作模式之前检查成员是否为 null,从而确保不会重新创建 ActionMode 实例(如果该实例已处于活动状态)。

创建弹出式菜单

显示 Gmail 应用中弹出式菜单的图片,该菜单锚定于右上角的溢出按钮。
图 4. Gmail 应用中的弹出式菜单,锚定到右上角的溢出按钮。

PopupMenu 是锚定到 View 的模态菜单。如果空间足够,它显示在锚点视图下方,否则显示在锚点视图上方。它适用于以下情况:

  • 为与特定内容(例如 Gmail 的电子邮件标头)相关的操作提供溢出样式菜单,如图 4 所示。
  • 提供命令语句的另一部分,例如标记为 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" />

稍后,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;
    }
}

创建菜单组

菜单组是指一系列具有某些共同特征的菜单项。借助群组,您可以执行以下操作:

您可以通过将 <item> 元素嵌套在菜单资源中的 <group> 元素内,或使用 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,还是其他应用中的 activity。如果您知道自己想要使用的 intent 并且具有可启动该 intent 的特定菜单项,则可以在相应的 on-item-selected 回调方法(例如 onOptionsItemSelected() 回调)期间使用 startActivity() 执行 intent。

不过,如果您不确定用户的设备是否包含可处理 intent 的应用,则添加调用 intent 的菜单项可能会导致菜单项无法正常工作,因为 intent 可能无法解析为 activity。为了解决此问题,当 Android 在设备上找到可处理 intent 的 activity 时,Android 允许您将菜单项动态添加到菜单中。

如需根据接受 intent 的可用 activity 添加菜单项,请执行以下操作:

  1. 使用类别 CATEGORY_ALTERNATIVE 和/或 CATEGORY_SELECTED_ALTERNATIVE 以及任何其他要求定义 intent。
  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;
}

对于每个发现的 activity 提供了与所定义的 intent 匹配的 intent 过滤器,系统将添加一个菜单项,使用 intent 过滤器的 android:label 中的值作为菜单项标题,使用应用图标作为菜单项图标。addIntentOptions() 方法会返回已添加的菜单项数量。

允许将您的 activity 添加到其他菜单中

您可为其他应用提供您的 activity 服务,以便将您的应用纳入其他应用的菜单中,从而与上述角色相反。

若要包含在其他应用菜单中,请照常定义一个 intent 过滤器,但为 intent 过滤器类别添加 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>

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