辅助功能*

Charles Ward

Charles Ward

Adobe

出版日期:
2008 年 2 月 25 日
用户级别:
中/高级
产品:
Adobe AIR

向 AIR 应用程序添加原有菜单

操作系统提供了内置 (即原有) 工具用于创建菜单。AIR NativeMenu 类提供一个界面, 用于创建和修改操作系统原有的菜单, 以及添加事件侦听器以处理菜单事件。

图 1 中显示的 AIRMenus 范例应用程序演示如何创建 Adobe AIR 支持的各种原有菜单。

Air 菜单

图 1。本范例应用程序演示如何处理原有菜单。

注意: 本范例应用程序按原样提供, 用于教学目的。

要求

若要充分利用本篇文章, 您需要以下软件和文件:

Adobe AIR

Adobe AIR SDK

范例文件:

本范例应用程序包括以下文件:

  • AIRMenus.js: 应用程序主脚本文件。
  • AIRMenus.html: 示例 HTML 页。
  • AIRAliases.js: 别名定义的列表, 这些别名定义将 AIR 类引用由长形式 (例如 window.runtime.flash.package.Class) 变为短形式 (例如 air.Class)。
  • AIRMenus-app.xml: 应用程序描述符文件。
  • AIR 图标文件范例

必备知识

应具备构建 HTML 应用程序的一般经验。有关使用此快速入门指南的详细信息, 请参阅用 HTML 构建快速入门范例应用程序

关于 AIR 菜单

Adobe AIR 支持以下类型的菜单:

  • 应用程序菜单: 仅 Mac OS X 支持, 应用程序菜单出现在操作系统提供的全局菜单栏中。Mac OS X 为每个应用程序都提供一个默认菜单。AIR 应用程序可以通过向相关的菜单命令添加事件侦听器来使用默认菜单, 通过添加和删除命令和子菜单来修改默认菜单, 还可以将默认菜单替换为新菜单。 (本 AIR Menus 范例采用的方式为替换默认菜单。)
    通过 air.NativeApplication.nativeApplication.menu 属性访问应用程序菜单。使用 air.NativeApplication.supportsMenu 属性测试应用程序所运行的环境中是否支持应用程序菜单。
  • 窗口菜单: 仅 Microsoft Windows 操作系统支持, 窗口菜单出现在窗口的标题栏下方。由于并不创建默认的菜单对象, 因此必须初始化新的 NativeMenu 对象才能使用窗口菜单。
    通过原有窗口对象的菜单属性访问窗口菜单。使用 NativeWindow.supportsMenu 属性测试应用程序所运行的环境中是否支持窗口菜单。窗口菜单只能用于具有系统镶边的窗口。
  • 上下文菜单: 对页面上大多数元素都提供默认的上下文菜单。通过将 oncontextmenu 事件侦听器附加至元素、取消默认行为并显示菜单, 可以使用不同的上下文菜单。此类菜单可以是用 NativeMenu.display() 方法弹出的原有菜单, 还可以创建 DHTML 菜单。
  • 停靠栏图标菜单: 在 Mac OS X 中, 可以向应用程序停靠栏图标添加菜单。新菜单中的菜单项出现在操作系统默认菜单项的上方。
  • 系统任务栏图标菜单: 在 Windows 中, 可以向系统任务栏图标添加菜单。并不创建默认菜单。
  • 弹出菜单: 通过调用任何 NativeMenu 对象的 display() 方法可以在应用程序中任何位置显示的菜单。

AIR Menus 范例应用程序

AIR Menus 范例应用程序创建一个包含 File (文件) 、Edit (编辑) 和 Help (帮助) 示例子菜单的原有菜单, 并将该菜单用于每个受支持的菜单位置。选择菜单命令后, 菜单事件处理函数就会将该命令的标签打印至窗口。

若要测试应用程序, 请下载并启动安装程序 (AIRMenus.air)。通过以下的一种方法打开菜单:

  • 从应用程序菜单栏或窗口菜单栏选择菜单项。
  • 在窗口中单击右键或按住 Command 键并单击, 打开上下文菜单。
  • 在窗口中单击或单击左键, 打开弹出菜单。
  • 单击并按住停靠栏图标, 打开停靠栏菜单。
  • 右键单击系统任务栏图标, 打开系统任务栏菜单。

无论何时选择菜单命令, 都会在窗口中列出该命令。

了解代码

有关使用 AIR 类的详细信息, 请参阅《为 HTML 开发人员提供的 Adobe AIR 语言参考》*

创建菜单

若要创建菜单, 首先请创建新的 NativeMenu 对象, 然后向其添加命令、子菜单和分隔符。应用程序和窗口菜单的顶 (即根) 级菜单为菜单栏, 并且应该只包含子菜单。在 Mac OS X 中完全不显示根级菜单中的命令项和分隔符项。在 Windows 中显示此类项目, 但从该项目不能打开子菜单, 这可能会使用户感到迷惑。

AIR Menus 范例使用一个函数创建根菜单, 而对 AIR 所支持的每种菜单类型都调用该函数。该函数创建三个示例子菜单, 标签分别为 File、Edit 和 Help。接下来由其它函数创建这些子菜单的 NativeMenu 对象:

function createRootMenu(){
    var menu = new air.NativeMenu();
    menu.addSubmenu(createFileMenu(),"File");
    menu.addSubmenu(createEditMenu(),"Edit");
    menu.addSubmenu(createHelpMenu(),"Help");
    return menu;
}

创建子菜单的函数使用 addItem() 方法添加命令和分隔符。每个 menu 对象都包含一个 items 数组。由于在选择菜单时调度的所有事件都由相同函数处理, 因此使用 items 数组将侦听器附加至所有项目。以下函数创建 File 菜单:

function createFileMenu(menuType){
    var temp;
    var menu = new air.NativeMenu();
    temp = menu.addItem(new air.NativeMenuItem("New"));
        temp.keyEquivalent = 'n';
        temp.data = menuType;
    temp = menu.addItem(new air.NativeMenuItem("Open"));
        temp.keyEquivalent = 'o';
        temp.data = menuType;
    temp = menu.addItem(new air.NativeMenuItem("Save"));
        temp.keyEquivalent = 's';
        temp.data = menuType;

    menu.addItem(new air.NativeMenuItem("",true));//分隔符

    temp = menu.addItem(new air.NativeMenuItem(quitLabel));
        temp.keyEquivalent = 'q';
        temp.data = menuType;
  
    for (var item = 0; item < menu.items.length; item++){
        menu.items[item].addEventListener(air.Event.SELECT,itemSelected);
    } 
    return menu;
}

此函数为每个菜单创建一个临时项目用于分配属性。通过设置项目的 keyEquivalent 属性可以分配快捷键。AIR 自动向快捷键添加标准功能键。在 Mac OS X 中, 此功能键为 Command 键;在 Windows 中, 此功能键为 Ctrl 键。此外, 如果用大写字母设置 keyEquivalent, 则还会将 shift 键添加至功能键数组。若要使用不带功能键的快捷键, 请使用小写字母, 并将 keyEquivalentModifiers 属性设置为空数组, 如下所示:

item.keyEquivalentModifiers = [];

注意: 等效键只能用于选择应用程序或窗口菜单中的命令。虽然可以分配等效键, 甚至可以显示这些等效键, 但在其它类型的菜单中, 按这些组合键不起所用。如果也有窗口菜单或应用程序菜单处理快捷方式, 则在上下文菜单或弹出菜单中显示快捷方式不一定会出现问题, 但在其它情况下显示不起所用的快捷方式可能会使应用程序用户感到迷惑。

createFileMenu() 函数还设置每个菜单项的 data 属性。通过 data 属性, 可以方便地引用与菜单命令相关的对象。在本例中, 将 data 属性设置为描述父菜单的字符串。在 itemSelected() 事件处理函数中使用此字符串报告所选命令所属的菜单。

设置应用程序和停靠栏菜单

AIR 支持操作系统中的应用程序菜单时, 静态 air.NativeApplication.supportsMenu 属性为 true。Mac OS X 操作系统提供默认的应用程序菜单。可以使用所提供的菜单 (大部分命令只有在添加事件侦听器后才会起作用), 也许还能添加新项目和子菜单, 或替换整个菜单。AIR Menus 范例采用第二种方法。

if(air.NativeApplication.supportsMenu){
    air.NativeApplication.nativeApplication.menu = createRootMenu();
}

AIR 支持操作系统中的停靠栏图标时, 静态 air.NativeApplication.supportsDockIcon 为 true。停靠栏图标由 air.NativeApplication.nativeApplication.icon 属性表示。自动创建图标对象。

Mac OS X 为停靠栏图标提供一个默认菜单。通过向 NativeMenu 对象添加项目, 并将该对象赋给 icon.menu 属性, 可以向停靠栏菜单添加其它项目。

if(air.NativeApplication.supportsDockIcon){
    air.NativeApplication.nativeApplication.icon.menu = createRootMenu();
    var dockIconLoader = new runtime.flash.display.Loader();
    dockIconLoader.contentLoaderInfo.addEventListener(air.Event.COMPLETE, iconLoadComplete);
    dockIconLoader.load(new air.URLRequest("icons/AIRApp_128.png"));
}

若要加载图标的图像, 请使用 air.Loader 类。如果 air.URLRequest 对象包含图像的 URL 字符串, 则此类加载该图像。由于加载器是异步的, 因此必须向 loader 对象的 contentLoaderInfo 属性添加事件侦听器, 才能检测到图像文件何时加载完毕。事件的处理函数通过设置 icon.bitmaps 属性, 将所加载的图像分配给图标。

var iconLoadComplete = function(event){
    air.NativeApplication.nativeApplication.icon.bitmaps = new runtime.Array(event.target.content.bitmapData);
}

无法直接从 HTML 页所加载的图像中获取 BitmapData 对象。但是, 可以使用 img 元素的 src 属性, 通过 air.Loader 类重新加载图像。

设置窗口菜单和系统任务栏菜单

AIR 支持操作系统中的窗口菜单时, 静态 NativeWindow.supportsMenu 属性为 true。Windows 操作系统不提供默认窗口菜单, 因此必须向原有窗口的 menu 属性赋予新的菜单对象:

if(air.NativeWindow.supportsMenu){
    window.nativeWindow.menu = createRootMenu();
}

AIR 支持操作系统中的系统任务栏图标时, 静态 air.NativeApplication.supportsSystemTrayIcon 为 true。系统任务栏图标由 air.NativeApplication.nativeApplication.icon 属性表示。自动创建图标对象。若要在任务栏的通知区域中显示图标, 只需要将包含图标图像的数组赋给图标的 bitmaps 属性。若要从任务栏删除图标, 请将 bitmaps 设置为空数组。

通过将 NativeMenu 对象赋给 icon.menu 属性, 向系统任务栏图标添加菜单。

if(air.NativeApplication.supportsSystemTrayIcon){
    air.NativeApplication.nativeApplication.icon.tooltip = "AIR Menus";
    air.NativeApplication.nativeApplication.icon.menu = createRootMenu();
    var systrayIconLoader = new runtime.flash.display.Loader();
    systrayIconLoader.contentLoaderInfo.addEventListener(air.Event.COMPLETE, iconLoadComplete);
    systrayIconLoader.load(new air.URLRequest("icons/AIRApp_16.png"));
}

请谨防在错误的操作系统上使用 SystemTrayIcon 属性。例如, 在 Mac OS X 中, air.NativeApplication.nativeApplication.icon 对象为 DockIcon 类型。尝试设置工具提示可能会产生运行时错误。

iconLoadComplete() 函数以与设置停靠栏图标相同的方式来设置系统任务栏图标的图像。

设置弹出菜单

通过调用 NativeMenu 对象的 display() 方法, 可以在窗口上任意位置显示菜单。必须将 stage 参数设置为显示菜单的 nativeWindow (几乎始终是显示 HTML 页的窗口) 的 stage 属性。position 参数指定相对于窗口左上角要显示菜单的位置。通过鼠标事件的 clientXclientY 属性, 在为了响应鼠标事件而打开菜单时可以方便地获取鼠标的坐标。

function popUpMenu(event){
    popUp.display(window.nativeWindow.stage, event.clientX, event.clientY);
}

弹出菜单的根菜单中可以放置命令、子菜单和分隔符。

设置上下文菜单

通过响应 oncontextmenu 事件, 可以显示 HTML 元素的上下文菜单。

<div oncontextmenu="showContextMenu(event)" style="-khtml-user-select:auto;">

可以用菜单对象的 display() 方法显示原有菜单, 还可以使用 DHTML 菜单。

在 AIR 中, 对于图像和所选文本有默认的上下文菜单。如果要显示自己的上下文菜单, 则应该调用 oncontextmenu 事件的 preventDefault() 方法, 阻止默认菜单弹出。AIR Menus 范例有两个具有上下文菜单的段落。其中一个显示默认菜单, 另一个显示自定义菜单。通过设置样式 -khtml-user-select:none;, 对其余的页面元素禁止选择文本, 因此无法打开上下文菜单。

用以下函数显示自定义的上下文菜单:

function showContextMenu(event){
    event.preventDefault();
    window.htmlLoader.contextMenu.display(window.nativeWindow.stage, event.clientX, event.clientY);
}

响应菜单事件

用户选择菜单项时, 菜单项调度 select 事件。AIR Menus 范例对每个项目都使用相同的事件处理函数。若要添加事件侦听器, 请调用项目的 addEventListener() 方法:

item.addEventListener(air.Event.SELECT,itemSelected); 

Select 事件还会沿菜单层次结构逐级而上。链中的每个父菜单也会调度 select 事件。事件对象的 target 属性是所选命令的 NativeMenuItem 对象, 而 currentTarget 属性是当前菜单的 NativeMenu 对象。

此示例通过向页面的 eventLog 部分添加描述事件的行, 响应每个 select 事件。

function itemSelected(event){
    var message = "Selected item: \"" + event.target.label + "\" in " + event.target.data + ".";
    air.trace(message);
    newEntry = document.createElement('p');
    newEntry.appendChild(document.createTextNode(message));
    eventLog.appendChild(newEntry);
}

同样提供、但在本范例中未使用的还有菜单的 displaying 事件。紧接菜单显示之前由该菜单调度 displaying 事件。可以使用 displaying 事件更新菜单中的项目, 以反映应用程序的当前状态。例如, 如果应用程序使用了一个菜单, 让用户可以打开最近查看过的文档, 则可以更新该菜单以反映文档的最新列表。

关于作者

Charles Ward 是一名技术作者, 它喜欢钻研新技术。在之前的 (专业) 职业生涯中, 他曾参与创建开拓性的计算机游戏, 如 Falcon 3.0 和 4.0 以及 Star Trek: A Final Unity。在闲暇时间, Charles 喜欢自由潜水以及和他的孩子们玩耍。