从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-3. 菜单)

第3章 菜单

在许多不同类型的应用中,菜单通常是一种用户界面组件。为了提供给用户提供熟悉且一致的体验,你需要使用菜单API来展示用户动作和你Activity中的其他选项。

从安卓3.0系统(API level 11)开始,安卓设备已经不再需要提供专用的菜单按键。基于这种变化,安卓应用需要远离原来所依赖的传统6选项菜单盘,取而代之的是提供一个动作条来显示普通用户的动作。虽然设计方案和用户使用菜单选项的方式已经改变,但是从语义上定义的一套动作和选项仍然是基于菜单API的。这份指导书将介绍在所有版本的安卓系统中如何去创建三个基本类型的菜单和动作:

1. 选项菜单和动作条

选项菜单对于一个应用的菜单项来说是首要的。你放置其中的动作一般是可以影响整个应用的,例如“搜索”、“写邮件”和“设置”。假如你为2.3或者更低版本的安卓系统开发应用,那么用户可以通过点击菜单按钮来显示选项菜单盘。在安卓3.0或者更高的系统中,选项菜单中的选项作为屏幕上动作项和溢出的选项采用动作条显示。从安卓3.0开始,菜单按键是不被赞成的(一些设备一个也没有),所以你需要改为使用动作条来提供动作和其他选项的入口。

2. 上下文菜单和上下文的操作模式

上下文菜单是一种浮动的菜单,是在当用户在一个元件上执行长按动作时显示的。当开发平台为安卓3.0或者更高的时候,你需要使用上下文操作模式来使所选的内容产生动作。这种模式显示的动作项会影响到在屏幕顶部条上选定的内容,并允许用户选择多项。

3. 弹出窗口菜单

弹出窗口菜单显示一列被锚记为调用菜单列表的列表项。它很好的提供了一个涉及到具体内容或者提供一个命令的第二部分选项的溢出操作。在弹出菜单中的动作不会直接影响到相应的内容,这就是上下文操作所想要的。

3.1 在XML中定义菜单

针对所有的菜单类别,安卓系统都提供了一个标准的XML格式来定义菜单项。你可以在一个XML菜单资源中定义一个菜单和它的所有选项,取代了在activity代码中建立菜单。你可以接着在你的活动中或者代码段中扩展菜单资源(载入它作为一个菜单对象)。

使用菜单资源是一个很好的惯例,主要有几个原因:

◆它更容易在XML中形象化菜单结构。

◆它把菜单的内容从你应用的行为代码中脱离出来。

◆它允许你创建交替的菜单结构以适应不同平台版本,屏幕大小,和其他利用应用资源框架的结构。

定义一个菜单,需要在你项目的res/menu/目录下创建一个XML文件以及使用下面这些元件创建菜单:

<menu>

定义一个菜单作为菜单项的容器。<menu>必须作为文件的根结点,这样才能容纳一个或多个<item>和<group>元素。

<item>

创建一个在菜单中表示一个单独的选项的菜单项。这个元素可能需要包含<menu>网来创建一个子菜单。

<group>

<item>元件中可选且不可见的容器。它允许你去把菜单项归类,所以它们可以分享特性例如激活状态和可视状态。

这里是一个命名为 game_menu.xml 的菜单实例,如代码清单3-1所示:

<?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>

代码清单3-1

<item>元件支持多种属性,你可以用来定义一个项的样式和行为。菜单上的选项包含了以下属性:

android:id

菜单项唯一的的ID资源,当用户选中这个选项时允许应用通过这个ID来识别这个菜单项。

android:icon

索引一个图片资源作为该项的图标。

android:title

索引一个字符串作为该项的标题

android:showAsAction

载明该项作为一个行为项什么时候和怎样显示在动作条中。

这些是你需要使用的最重要属性,但是还有更多可用的属性。

你可以通过增加一个<menu>元素作为<item>的子项,给任意菜单的项增加子菜单(除了子菜单本身以外)。当你的应用有大量的功能被组织成主题形式,例如电脑应用程序的菜单栏的选项(文件,编辑,查看等等)时子菜单是非常有用的。

如代码清单3-2所示:

<?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>


代码清单3-2

在你的应用中使用菜单,你可以使用MenuInflater.inflate()寻找需要的菜单资源文件(将文档资源转换成一个可编程的对象)。在接下来的章节,你将看到怎样为每个菜单类型定位菜单文件。

3.2 创建选项菜单

选项菜单包含了动作以及其他与当前活动上下文相关的选项,例如"搜索","撰写邮件"以及"设置"等.在你选项菜单中的选项出现在屏幕中的位置时根据你开发应用的版本而定的:

◆如果你开发的应用是基于Android 2.3.x(API级别10)或者更低的,那么当用户点击菜单按钮时你的选项菜单的内容出现在屏幕的底部。例如图像1.当菜单打开时,首先看到的是菜单图标的部分,且最多可容纳六个菜单项。如果你的菜单包含了多于六的菜单项,那么Android放置六个选项且其余的放入到溢出的菜单中,用户可以通过选择“更多”选项来打开(如图3-1)。

 

图 3-1 android2.3下系统自带浏览器选项菜单

◆如果你开发的应用是基于Android 3.0(API级别11)或者更高的,那么菜单选项中的选项可以添加到动作条中。默认情况下,系统会放置所有选项在动作溢出栏中,用户可以打开在动作条右边的动作溢出图标(或者如果设备菜单按钮可用的话,用户可以点击它)。为了能够快速的访问重要的动作,你可以在相应的<item>元件中添加 android:showAsAction="ifRoom"使一些选项出现在动作条上(如图3-2)。更多关于动作项和其他动作条行为的信息可以查看Action Bar说明。

注意:即使你的开发没有基于Android 3.0或者更高版本,你也可以创建你自己的动作条布局取得相似的效果。

 

图 3-2 android3.0以上系统的动作条(加上了动作溢出按钮)

从你的Activity子类或者Fragment子类你可以声明选项菜单的选项。假如你的Activity和Fragment都声明了选项菜单的选项,那么它们将被集合在UI界面中。Activity的选项先显示,然后才是每个Fragment按顺序添加到Activity中。如果需要,你还可以在你需要移动的每个<item>元素中添加android:orderInCategory的属性重新按次序添加菜单项。

指定一个Activity的选项菜单,需要覆写 onCreateOptionMenu()这个方法(Fragment提供它们自己的onCreateOptionMenu()回调方法)。在这种方法下,你可以导入你的菜单资源(定义在XML文件中)到Menu提供的回调方法中。如代码清单3-3所示:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.game_menu, menu);
    return true;
}

代码清单3-3

 

你也可以使用add()方法添加菜单项以及使用findItem()方法取回选项,使用MenuItem APIs修改它们的属性。

如果你已经开发了基于Android 2.3.x或者更低版本的应用,那么当用户第一次打开菜单时系统可以调用onCreateOptionMenu()去创建选项菜单。

如果你已经开发的应用是基于Android 3.0或者更高版本的,当活动(Activity)启动时系统可以调用onCreateOptionsMenu()在动作条上显示选项。

3.2.1单击事件处理

当用户从选项菜单中选择一个选项时(包括动作条中的动作选项),系统将会调用你的活动中的onOptionsItemSelected()方法。这种方法是通过MenuItem选择的。你可以通过调用getItemId()来识别选项,那么它会返回一个菜单项中特定的ID(定义在菜单资源中的android:id或者通过add()方法赋予其一个整型数)。你可以对已知的菜单项来匹配这个ID去执行适当的动作。如代码清单3-4所示:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.new_game:
            newGame();
            return true;
        case R.id.help:
            showHelp();
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}


代码清单3-4

当你成功的处理了一个菜单项,则会返回true值。如果你不能处理一个菜单项,那么你需要调用onOptionsItemSelected()基类来实现(默认实现方法返回false值)

如果你的Activity包含Fragment,那么系统首先会为Activity调用onOptionsItemSelected(),然后才是每个Fragment(每个Fragment已经按顺序添加)直到有一个返回true值,或者所有的片段都被调用了。

提示:Android 3.0在XML中的菜单项中为你增加了定义点击行为的能力,使用android:onClick属性即可。属性的值必须使用菜单的Activity中定义的方法的名称。当系统调用这个方法的时候,方法必须是公共的且接受单独的MenuItem参数。且它是通过菜单项选择的。

提示:如果你的应用包含多个Activities且其中有一部分提供相同的选项菜单,你可以创建一个Activity只实现onCreateOptionsMenu()和onOptionsItemSelected()这两个方法。然后每个需要使用这个选项菜单的Activity继承这个类。这样的话,你可以管理一部分代码来处理菜单动作且每个子类都集成菜单的动作。如果你想要给子Activity增加菜单项,只要在这个Activity中覆写onCreateOptionsMenu()这个方法即可。调用super.onCreateOptionsMenu(menu)那么原菜单项就被创建了,然后使用menu.add()添加新的菜单项.当然你也可以为个别的菜单项复写基类的动作。

3.2.2运行时改变菜单项

在系统调用onCreateOptionsMenu()之后,它保留了你填充菜单中的一个实例且不会再调用onCreateOptionsMenu(),除非菜单出于某种原因失效了。然而,你可以使用onCreateOptionsMenu()去创建一个有效的菜单状态且不能在Activity的生命周期内改变。如果你想要在Activity的生命周期内基于事件修改选项菜单,你需要在onPrepareOptionsMenu()方法中实现。只有目前存在菜单对象这个方法才能通过,那么你就可以修改它,例如增加,移除或者使选项失效。(Fragment也可以提供一个onPrepareOptionsMenu()的回调)。

在Android 2.3.x或者更低的版本中,当用户每次打开选项菜单(通过点击菜单按钮)时系统都会调用onPrepareOptionsMenu()。在Android 3.0或者更高版本中,当菜单项提交在动作条时选项菜单被认为是始终打开的。当一个事件发生且你想要执行一个菜单的更新,那么你必须调用invalidateOptionsMenu()这个方法去反馈系统调用的onPrepareOptionsMenu()方法。

注意:你永远不应该改变基于在选项菜单中目前处于焦点View的的选项。当其处于触点状态(用户没有使用循迹球或者d-pad),视图不能处于焦点状态,所以你永远不能在选项菜单中使用焦点来改变选项。假如你想要为视图(View)提供一个上下文敏感的菜单项,请使用上下文菜单。

3.3 创建上下文菜单

一个上下文菜单可以提供影响一个特殊选项或者UI中上下文的框架的动作。你可以为任何界面提供一个上下文菜单,但是它们通常在ListView、GridView或者用户可以在每个选项中直接执行的动作的其他界面分类中使用。这里有两种方法提供一个上下文动作:

◆在浮动的上下文菜单中。当用户在一个声明支持上下文菜单的界面中执行一个长点击(按住并保持),那么这个菜单将作为一个菜单项浮动列表显示(类似对话框)。用户可以每次在一个选项上执行一个上下文动作。

◆在上下文动作模式下。这种模式是系统实现的动作模式,在屏幕顶部显示上下文动作条动作条影响所选选项的动作选项的模式。当这种模式被激活,用户可以一次在一个动作中执行多个选项(如果你的应用允许这样)。

注意:上下文动作模式只有在Android 3.0或者更高版本可用且当可用时是首选的显示上下文动作的技术。如果你的应用支持的版本低于3.0,那么你需要在这些设备中使用浮动的上下文菜单。效果如图3-3所示:

 

图 3-3 浮动上下文菜单截图(左边)和上下文动作条(右边)

3.3.1创建浮动的上下文菜单

为了提供一个浮动的上下文菜单:

1. 通过调用resisterForContextMenu()来注册上下文菜单相关的View。

如果你的Activity使用了ListView或者GridView且你想要每个选项都提供一个相同的上下文菜单,那么需要通过调用ListView或者GridView中的registerForContextMenu()为一个上下文菜单注册所有的选项。

2. 在你的Activity或者Fragment实现onCreateContextMenu()的方法。

当注册时接收到一个长点击事件,那么系统将会调用你的onCreateContextMenu方法。这是你定义菜单项的地方,通常通过导入一个菜单资源。如代码清单3-5所示:

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


代码清单3-5

MenuInflater允许你从一个菜单资源导入上下文菜单。这种回调方法的参数包含了用户选择的View和提供关于被选项额外信息的ContextMenu.ContextMenuInfo对象。假如你的Activity有若干个提供不同上下文菜单的视图,你需要使用这些参数来确定被导入的上下文菜单。

3. 实现onContextItemSelected()。

当用户选择一个菜单项时,系统调用这个方法则你可以执行相应的动作。如代码清单3-6所示:

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


代码清单3-6

getItemId()方法为被选的菜单项查询ID,这些ID是你在XML文件中使用android:id属性分配给每个菜单项的。如在XML中定义一个菜单的章节中所讲的。

当你成功的操作了一个菜单项,将返回true值,如果你不能操作菜单项,你需要通过基类来实现菜单项。如果你的Activity包含Fragment,那么Activity将首先接收到这个回调。当不能操作时通过调用基类,系统将会在每个Fragment中使用各自的回调方法过掉这个事件,一次一个(每个Fragment都已经被按顺序添加了)直到返回true或者false已经返回了(默认Activity和Fragment实现时返回false,当不能操作时你总是需要调用基类)。

3.3.2使用上下文动作模式

上下文动作模式是系统实现的动作模式,重点是在执行上下文动作的用户交互。当一个用户通过选择一个选项使这个模式启用,那么上下文动作条将会出现在呈现用户可执行的当前被选选项的屏幕的顶部。当这种模式被启用,那么用户可以选择多个选项(如果你允许这样的话),取消多个选项以及继续在活动中导航(你允许的尽可能多的)。当用户通过点击返回按钮取消选择所有选项或者选择动作条坐标的完成动作,则动作模式失效且上下文动作条消失。注意:上下文动作条没有必要与动作条相关联。它们是独立运行的,即时上下文动作条显示超过动作条的位置。如果你正在开发基于Android 3.0或者更高版本的应用,通常你需要使用上下文动作模式代替浮动上下文菜单来呈现上下文动作。

为提供上下文的视图,通常你需要调用上下文的动作模式如以下两个事件:

◆用户在界面中执行长点击。

◆用户选择一个复选框或者视图中类似的UI组件。

如何使你的应用调用上下文动作模式且根据你的设计为每个动作定义行为。基本上有两种设计:

◆    对于个别的任意视图的上下文动作

◆    对于在ListView或者GridView的选项组的一些上下文动作(允许用户选择多个选项并执行它们所有的动作)。

接下来的部分是描述各个方案所需的设置。

3.3.3为个别View启用上下文动作模式

假如仅仅当用户选择特殊视图时你想要调用上下文动作模式,那么你应该:

◆实现ActionMode.Callback接口。在它的回调方法中,你可以为上下文动作条指定动作,相应在动作选项上点击事件,以及为动作模式操作其他生命周期事件。

◆当你想要显示一个动作条(例如当用户长点击视图)时可以调用startActionMode()方法。

例如:

1. 实现ActionMode.Callback接口,如代码清单3-7所示:

private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
     @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        MenuInflater inflater = mode.getMenuInflater();
        inflater.inflate(R.menu.context_menu, menu);
        return true;
    }
 
   
    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        return false;
    }
 
    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_share:
                shareCurrentItem();
                mode.finish(); 
                return true;
            default:
                return false;
        }
    }
 
    @Override
    public void onDestroyActionMode(ActionMode mode) {
        mActionMode = null;
    }
};


代码清单3-7

请注意,这些回调事件几乎和选项菜单回调一模一样的,除了这些还可以通过与事件相关的ActionMode对象。你可以使用ActionMode APIs来使CAB各种变化,例如使用setTitle()和setSubtitle()来修改标题和子标题(所有被选中的选项都可用)。

同时也请注意上述的例子在当动作模式被注销时设定了mActionMode变量为空。下一步,你将会看到它是如何初始化以及如何在你可用的Activity或Fragment中保存成员变量。

2. 当需要时调用startActionMode()去启用上下文动作模式,例如相应一个视图中的长点击,如代码清单3-8所示:

someView.setOnLongClickListener(new View.OnLongClickListener() {
    public boolean onLongClick(View view) {
        if (mActionMode != null) {
            return false;
        }
        mActionMode = getActivity().startActionMode(mActionModeCallback);
        view.setSelected(true);
        return true;
    }
});


代码清单3-8

当你调用startActionMode(),系统返回ActionMode被创建。通过保存这个成员变量,你可以在相应其他事件时改变上下文动作条。在上述例子中,ActionMode是被用于确保ActionMode实例不会被重建,在它已经激活的情况下,且在动作模式开始的时候检查成员是否为空。

3.3.4为在ListView或者GridView中启用一批上下文动作

如果在ListView或者GridView(或者另一个AbsListView的扩展类)中你有一个选项分类,且想要允许用户去执行一批动作,你需要:

◆实现AbsListView.MultiChoiceModeListener接口且使用setMultiChoiceModeListener()把它设定给一个视图组。在监听回调的方法的时候,你可以为上下文动作条指定动作,在动作项中相应点击事件,或者操作来自ActionMode.Callback接口的其他回调。

◆使用CHOICE_MODE_MULTIPLE_MODAL调用setChoiceMode()。

如代码清单3-9所示:

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) {
    }
 
    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_delete:
                deleteSelectedItems();
                mode.finish();
                return true;
            default:
                return false;
        }
    }
 
    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        MenuInflater inflater = mode.getMenuInflater();
        inflater.inflate(R.menu.context, menu);
        return true;
    }
 
    @Override
    public void onDestroyActionMode(ActionMode mode) {
      
    }
 
    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
     
        return false;
    }
});


代码清单3-9

就是这样,现在当用户通过长点击选择一个选项时,系统将会调用onCreateActionMode()方法且为指定的动作显示一个上下文动作条。当上下文动作条可见时,用户可以选择附加的选项。

在上下文动作提供共同的动作选项的一些情况下,你可能想要添加一个复选框或者相似的允许用户选择的选项的UI组件,因为它们也许不能通过长点击行为发现。当一个用户选择复选框时,你可以通过使用setItemChecked()为各自的选项列表设定选择状态来调用上下文动作模式。

3.4 创建弹出菜单

弹出菜单是一个形式上的菜单标记在View上面。它出现在定位的窗口之下,如果那里有空间的话,或者在其上面。它对以下这些是有用的:

◆    为关联到特殊内容的动作提供一个溢出模式的菜单(例如Gmail的邮件头部,如图3-4所示)。

 

图3-4 在Gmail应用里的弹出菜单,从右上角浮出。

注意:这是不同于会影响到所选内容的普通动作的上下文菜单。为了使动作能够影响所选内容,使用上下文动作模式或者浮动上下文菜单。

◆提供一个命令句的第二部分(例如一个标记为"Add"的按钮,使用不同"Add"选项可产生一个弹出菜单)。

◆提供一个类似不保留持续选项的Spinner的下拉菜单。

注意:弹出菜单在API 11或者更高的版本可用。

如果你在XML中定义你的菜单,这里介绍了你怎样去显示弹出菜单:

1. 使用其结构实例化一个弹出菜单,可以获取当前应用的Context和被标记的菜单的View。

2. 使用 MenuInflater来inflate 你的菜单资源到通过PopupMenu.getMenu()返回的菜单对象中。在API level 14或以上版本,你能使用PopupMenu.inflate() 代替。

3. 调用PopupMenu.show()。

如代码清单3-10所示,有一个按钮的点击事件:

<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" />

代码清单3-10

在Activity中需要这么写,如代码清单3-11所示:

public void showPopup(View v) {
    PopupMenu popup = new PopupMenu(this, v);
    MenuInflater inflater = popup.getMenuInflater();
    inflater.inflate(R.menu.actions, popup.getMenu());
    popup.show();
}

 

代码清单3-11

3.4.1处理单击事件

当用户选择一个菜单项去执行一个动作时,你必须调用setOnMenuItemclickListener()实现PopupMenu.OnMenuItemClickListener接口且用PopupMenu来注册它。当用户选择一个选项时,系统将在你的界面中调用onMenuItemClick()函数来回调。如代码清单3-12所示:

public void showMenu(View v) {
    PopupMenu popup = new PopupMenu(this, v);
    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;
    }
}


3.5 创建菜单组

菜单组是一个分享某些特征的菜单项集合,使用菜单组,你可以:

◆使用setGroupVisible()显示或隐藏所有选项;

◆使用setGroupEnabled()让所有选项有效或无效;

◆使用setGroupCheckable()指定是否所有选项可选。

在你的菜单资源或者使用add()方法指定的菜单组ID中,你可以在<group>元件里嵌套<item>元件来创建菜单组。这里有一个包含菜单组的菜单资源例子,如代码清单3-13所示:

<?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-13

这些在组里的选项与第一个选项一样的级别显示——在菜单里的所有三个选项是同级别的。然而,你可以通过查询组的ID地址和使用上面列出的方法来修改组里面两个选项的特征。系统还从未分离过组选项。例如,假如你为每一个选项声明android:showAsAction="ifRoom",那么他们将同时出现在动作条上或者动作溢出上。

3.5.1使用可选的菜单项

个菜单可以被当做选项开关的接口,独立选项使用复选框,或者互相排斥的选项。组使用单选按钮。如图3-5显示的是一个使用可选的单选按钮的子菜单。

 

图3-5 可选的子菜单的截图。

注意:在图标菜单里的菜单项(来自选项菜单)不能显示复选框或者单选按钮。假如你选择让图标菜单里的选项可选,那么你需要在每次状态改变时手动的改变图标或者文字来表示选择状态。你可以在<item>元素使用android:checkable属性来定义一个独立菜单项的可选行为,或者在<group>元素里使用android:checkableBehavior属性来为整个组定义。例如,这个菜单组里的所有选项使用单选按钮且可选,如代码清单3-14所示:

<?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>


代码清单3-14

android:checkableBehavior属性可设定为:

single:表示在菜单组中只有一个选项能被选中(单选按钮)

all:表示所有选项都可被选(复选框)

none:表示没有选项可选

你可以在<item>元素里使用android:checked属性来定义选项的默认选择状态或者在代码中使用setChecked()方法来改变状态。当一个可选的项被选中,系统将调用选项各自的回调方法(例如onOptionsItemSelected())。在这里你必须设定复选框的状态,因为复选框或者单选按钮无法自动改变它们的状态。你可以使用isChecked()查询选项的当前状态,以及使用setChecked()来设定它选中状态。例如代码清单3-15所示:

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


代码清单3-15

假如你不通过这种方式来设定选中状态,那么选项的可视状态(复选框或者单选按钮)在用户选中它时将不会改变。当你设定了状态,activity将维持选项的选中状态以至于当用户较迟打开菜单,选中状态你设定为可见。注意:可选菜单项是被拟用于每个会话基础上的,且不能在应用销毁后保留。假如你想为用户保存应用设定信息,你需要使用Shared Preferences保存数据。

3.6 基于一个Intent添加菜单选项

一些时候你可能想要使用intent来载入一个Activity(无论这个Activity是在你的应用中或者在其他应用中)。当你知道你想要使用的intent且有一个可以开启这个Intent的具体菜单项时,你可以在合适的选项被选中回调方法时使用startActivity()来开启这个Intent(例如onOptionsItemSelected()回调)。然而,假如你不确定用户的设备是否包含有处理这个Intent的应用,却还添加了一个菜单项来调用它,这将导致生成一个无功能的菜单项,因为这个Intent可能无法在这个Activity中解决。为了解决这个问题,安卓系统可以允许你在设备发现发现了可以处理你的Intent的Activity时动态的添加菜单选项。以下基于可接受一个Intent的Activity的添加菜单方式:

1. 使用CATEGORY_ALTERNATIVE 和/或者 CATEGORY_SELECTED_ALTERNATIVE定义一个intent,可以添加其他任意需求。

2. 调用Menu.addIntentOptions()方法。安卓系统可以搜寻到任何能够执行这个Intent的应用,且将它们添加到你的菜单中。

 

假如系统中没有安装可以满足Intent的应用,那么将没有菜单选项可以被添加。

注意:CATEGORY_SELECTED_ALTERNATIVE是被用来处理当前在屏幕上被选中的元素的。所以,它只能在采用onCreateContextMenu()创建菜单时使用。如代码清单3-16所示:

@Override
public boolean onCreateOptionsMenu(Menu menu){
    super.onCreateOptionsMenu(menu);
    Intent intent = new Intent(null, dataUri);
    intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
    menu.addIntentOptions(
         R.id.intent_group,  // 将被添加到菜单组的item
,      // 唯一item ID (none)
,      // items顺序 (none)
         this.getComponentName(),   // 当前 activity name
         null,   // 指定第一次放置的位置 (none)
         intent, // 根据我们的需求,传入的intent
,      // 用来控制items的附加标志 (none)
         null);  // 关联指定items的菜单items数组 (none)
 
    return true;
}


代码清单3-16

为了让每个Activity都能被找到,那么需要提供一个匹配已定义的Intent的容器、一个被添加进来的菜单项、使用在intent容器中类似菜单项名字的android:label的值,以及类似菜单项图标的应用图标。addIntentOptions()方法将返回被添加进来的选项的数量。注意:当你调用addIntentOptions(),它将覆盖菜单组指定的所有菜单项。

3.6.1允许你的Activity添加到其他菜单

你可也可以为其他应用提供你的Activity中的服务,所以你的应用也可以被包含在其他应用的菜单中(反之亦可)。为了能够被添加到其他应用得菜单中,通常你需要定义一个intent filter,但是必须要包含 CATEGORY_ALTERNATIVE 和/或者 CATEGORY_SELECTED_ALTERNATIVE 的intent filter category(类别)的值。例如代码清单3-17所示:

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

 

代码清单3-17

你可能感兴趣的:(从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-3. 菜单))