Android应用程序用户界面(十二)菜单

菜单是很多类型的应用程序中的通用用户界面组件。为了提供一个相似和一致的用户体验,你应该使用菜单的API来提示用户可执行的动作或你的活动中的其他选项。

Android 3.0开始,使用Android系统的设备不再要求提供专门的菜单按钮。伴随着这种改变,Android应用程序应该不再依赖传统的六元素菜单面板,而是提供一个动作条来提示普遍的用户动作。

尽管某些菜单项的设计和用户体验已经改变,但是定义动作和选项集合的语义仍然依赖于菜单的API。这篇指导显示如何创建在Android的各个版本上创建3个基本类型的菜单或者动作提示:

  • 选项菜单和动作条
    选项菜单是一个活动的菜单项的集合。这是你应该放置对应用程序有全局影响的动作的地方,例如搜索、编辑邮件和设置等。
    如果你为Android 2.3或更低版本的Android系统开发应用程序,用户可以通过按菜单按钮来显示选项菜单。
    Android3.0或更高版本的Android系统中,选项菜单的菜单项是通过屏幕上可见的动作项和溢出的可选项组成的动作条表示。从Android3.0开始,菜单按钮不再建议使用(一些设备没有菜单按钮)。因此,你应该转而用动作条来提供动作或其他选项的访问。

  • 上下文菜单和上下文动作模式
    上下文菜单是一个浮动菜单,当用户长按一个元素时出现。它提供影响选择的内容或内容框架的动作。
    当为Android 3.0或更高系统开发应用程序时,你应该使用上下文动作模式来开启被选择内容的动作。这个模式在屏幕的顶端以动作条的方式显示了影响被选择内容的动作项,并且运行用户选择多个选项。

  • 弹出菜单
    弹出菜单以竖直的方向显示一列元素,并且停靠在触发该菜单的视图上。这是一种为特定内容提供溢出的动作或一个指令的第二个部分的方式。在弹出菜单中的动作不应该直接影响相应的元素(那是上下文菜单做的事),而是为你的活动中的内容域提供拓展的动作。

使用XML定义菜单

对于所有的菜单类型,Android提供了一个标准的XML格式定义菜单项目。不用在你的活动代码中构建菜单,你应该在一个菜单资源XML文件中定义菜单和它的所有项。然后你可以在你的活动或fragment中填充菜单资源(加载菜单对象)。

使用菜单资源是一个很好的方式,包括以下几点原因:

  • XML中可以简单地可视化菜单结构。
  • 将菜单的内容和应用程序的行为分离。
  • 允许为不同的平台版本、屏幕大小或其他配置创建适应的菜单配置。

为了定义菜单,在你的项目的res/menu/目录下创建一个XML文件,然后使用下面的元素构建菜单:


  • 定义菜单,是菜单项的容器。这个文件的根元素必须是一个元素,可以包含一个或多个group元素

  • 创建一个菜单项,代表了菜单中的单独的一项。为了创建子菜单,这个元素可以包含一个嵌套的元素。

  • 一个可选的、不可见的元素的容器。它允许你组织菜单项以在它们之间共享属性(例如激活状态和可见性等)。

下面是一个名为game_menu.xml的一个菜单的例子:


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

元素支持几个属性,你可以使用它们定义每个元素的外观和行为。上面的菜单中的菜单项包含下面的元素:

  • android:id
    菜单项的唯一资源ID,当用户选择它时,应用程序可以根据该ID识别出这个元素。
  • android:icon
    引用一个可绘制元素以作为该菜单项的图标。
  • android:title
    引用一个字符串作为该菜单项的标题
  • android:showAsAction
    指定何时和该项目如何作为动作项显示在动作条中。

这些是你应该使用的最重要的属性,当然还有很多其他的属性可以设置。

通过在元素下添加

子元素,可以在任何菜单的一个菜单项中添加子菜单。当你的应用程序有很多功能并且这些功能可以组织成不同的组时,子菜单是非常有用的,就像个人电脑应用程序中的菜单栏。例如:


<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/file"
          android:title="@string/file" >
        
        <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资源转换成一个程序化的对象)。

创建上下文菜单

上下文菜单提供影响用户界面中的特定元素或上下文框架的动作。你可以为任何View提供一个上下文菜单,但是它们常被用在ListViewGridView的元素或者其他的View集合上,在这些View集合中用户可以为每一个元素提供直接的动作。

有两种方式可以提供上下文动作:

  • 使用浮动上下文菜单。当用户长按一个声明支持上下文菜单的View时,菜单以一个浮动的菜单项的列表形式出现(类似于对话框)出现。用户可以执行一次为一个元素执行一个上下文动作。
  • 使用上下文动作模式。这个模式是一个ActionMode的系统实现,它在屏幕的顶端显示一个包含影响被选择元素的上下文动作条。当这个模式被激活时,用户可以一次为多个元素执行一个动作(如果你的应用程序允许的话)
    注意:上下文动作模式在Android 3.0(API 11)或更高版本的系统中可用,在可用的情况下,是推荐使用的显示上下文动作的技术。如果你的应用程序支持低于3.0Android系统版本, 那么你应该在这些设备上使用浮动上下文菜单。

Android应用程序用户界面(十二)菜单_第1张图片

创建浮动上下文菜单

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

  1. 通过以View对象为参数调用registerForContextMenu(),为View对象注册关联的上下文菜单。如果你的活动使用了ListViewGridView,并且你想要每一个元素提供相同的上下文菜单,则把ListViewGridView对象传递给registerForContextMenu()方法。
  2. 在你的活动或Fragment中实现onCreateContextMenu()方法。
    当被注册的视图接收到长按事件时,系统调用你的onCreateContextMenu()方法。在这个方法中定义菜单项,通常是以填充菜单资源的方式。
@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允许你从一个菜单资源中填充上下文菜单。回调函数参数包括用户选择的视图,为每一个被选择的元素提供额外信息的ContextMenu.ContextMenuInfo对象。如果你的活动有多个视图,每一个都有不同的上下文菜单,你可能需要使用这些参数来定义填充哪个上下文菜单。
3. 实现onContextItemSelected()
当用户选择一个菜单项时,系统调用这个方法,这样你就可以执行适当的动作了。例如:

@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属性分配的。

当你成功地处理了一个菜单项,返回true。如果你没有处理菜单项,你应该将菜单项传递给它的父类。如果你的活动包含Fragment,活动首先接收到这个回调函数。通过在不处理的时候调用父类,系统将事件传递给每个fragment的回调方法,一次一个(以每个fragment被添加的顺序),直到返回truefalse。(活动和Fragment的默认实现返回false,因此你总应该在不处理菜单项时,调用父类的处理函数)

使用上下文动作模式

上下文动作模式是ActionMode的系统实现,ActionMode关注于用户交互的上下文动作的执行方面。当用户通过选择一个元素开启了这个模式,一个上下文动作条出现在屏幕的顶端,以提示在当前选择的项上可以执行的动作。当这个模式开启时,用户可以选择多个元素(如果你允许的话)、取消选择并且继续在活动中Navigate(浏览)(你只要允许)。当用户没有选择元素、按了BACK按钮或选择处于动作条左边的完成动作时,动作模式禁用,上下文动作条消失。

注意:上下文动作条并不必要和动作条关联。他们独立操作,尽管上下文动作条可视化地占据动作条位置。

如果你为Android 3.0(API 11)或更高版本的Android系统开发应用程序,你应该总使用上下文动作模式来提示上下文动作,而不是浮动上下文菜单。

对于提供上下文动作的View对象,你应该总是在下面两个事件之一的情况下触发上下文动作模式:

  • 用户在View对象上执行了长按操作
  • 用户选择了View对象中的checkbox或相似的UI组件
    你的应用程序如何触发上下文动作模式和为每个动作定义行为依赖于你的设计。基本上分为以下两种:

  • 为单一的、任意的视图的上下文动作

  • ListViewGridView中的一组元素的批上下文动作(允许用户选择多个元素,在这些元素中执行同样的动作)

下面两节为每个想定描述设置需求。

(1)为单个View对象开启上下文动作模式
如果你想要只有当用户选择特定的元素时才触发上下文动作模式,你应该:
1. 实现ActionMode.Callback接口。在它的回调方法中,你可以指定上下文动作条中的动作,动作项对点击事件的响应和为动作模式处理其他生命周期事件。
2. 当想显示动作条时(例如当用户长按该视图),调用startActionMode()

例如:
实现ActionMode.Callback接口:

private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {

    // 当动作模式创建或`startActionMode`调用时调用
    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        // 填充提供上下文菜单项的菜单资源
        MenuInflater inflater = mode.getMenuInflater();
        inflater.inflate(R.menu.context_menu, menu);
        return true;
    }

    // 当每次动作模式显示时调用,总在onCreateActionMode被调用会调用,不过如果该模式无效,可能被调用多次.
    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        return false; // 如果什么都不做,返回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;
    }
};

注意这些事件回调几乎和选项菜单中的回调函数一样,除了每一个回调函数要传入一个与每个事件关联的ActionMode对象。你可以使用ActionModeAPI来对上下文动作条做各种各样的改变,例如使用setTitle()setSubTitle()方法修改标题或子标题。

也注意上面的例子当ActionMode被销毁时设置mActionMode变量为空。在下一步,你会看到在活动或Fragment中初始化和保存成员变量是多么有用。

2 在合适的时候调用startActionMode()开启上下文动作模式,例如响应一个View对象的长按事件:

someView.setOnLongClickListener(new View.OnLongClickListener() {
    // 当用户长按someView时调用
    public boolean onLongClick(View view) {
        if (mActionMode != null) {
            return false;
        }

        //使用上面定义的ActionModel.Callback启动上下文动作菜单 
        mActionMode = getActivity().startActionMode(mActionModeCallback);
        view.setSelected(true);
        return true;
    }
});

当你调用startActionMode()方法时,系统返回创建的ActionMode。通过将它保存在成员变量,你可以根据其他事件来改变上下文动作条。在上面的例子中,通过在开启动作模式之前检测,ActionMode成员变量是否为空,确保ActionMode实例不会在激活状态下被重新创建,。

(2)在ListViewGridView中开启批上下文动作
如果你在ListViewGridView中有一组元素(或者AbsListView的其他子类),并且想要用户执行批处理,那么你应该:

  • 实现AbsListView.MultiChoiceModeListener,并且使用setMultiChoiceModeListener()方法为视图组设置该Listener。在listener的回调方法中,你可以指定上下文动作条的动作,动作项的响应和继承自ActionMode.Callback的其他回调函数。
  • CHOICE_MODE_MULTIPLE_MODAL为参数值调用setChoiceMode()方法。

例如:

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

搞定。现在当用户长按选择一项时,系统调用onCreateActionMode()方法,并且显示带有特定动作的上下文动作条。当上下文动作条可见时,用户可以选择其他的元素。

在一些情况下,上下文动作提供了通用的动作项,你可能想要添加一个checkbox或者一个相似的UI元素,允许用户选择元素,因为它们可能未发现长按行为。当用户选择了checkbox,你可以通过设置相应的列表项目为checked状态使用setItemChecked()方法触发上下文动作模式。

原文

http://wear.techbrood.com/guide/topics/ui/menus.html#CAB

你可能感兴趣的:(Android)