辅助功能*

Charles Ward

Charles Ward

Adobe

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

自定义窗口的外观

关闭窗口的系统镶边可以非常充分地施展创造力, 但同时也会失去操作系统为标准窗口提供的自动管理窗口大小、位置和显示状态的功能。Custom Chrome 范例应用程序演示如何充分利用自建镶边的某些创造性优势, 以及如何替代操作系统不再提供的窗口管理服务。

此外, 本应用程序还演示如何将应用程序的一部分映射至非应用程序沙箱, 以及如何在应用程序沙箱中的父文档与非应用程序沙箱中的子文档之间建立一座桥梁。

Custom chrome 窗口

图 1。Custom Chrome 范例应用程序演示默认系统镶边的替代方案。

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

要求

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

Adobe AIR

Adobe AIR SDK

范例文件:

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

  • html/index.html: 应用程序主文件。
  • images/: 包含自定义镶边所使用图像的目录
  • css/chrome.css: 用于定义自定义镶边样式的应用程序样式表。
  • js/WindowControl.js: 用于根据用户与镶边元素的交互来控制窗口的函数。
  • js/AIRAliases.js: 定义 AIR API 短引用的标准 AIR 别名脚本。
  • sandbox/: 包含应用程序中映射至非应用程序沙箱部分的目录
  • sandbox/html/child.html: 应用程序中沙箱部分的主页。
  • sandbox/js/CanvasLissajousAnimation.js: 使用 Canvas API 绘制简单的动画。
  • sandbox/js/ContextMenu.js: 定义 DHTML 上下文菜单的代码。
  • sandbox/images/: 应用程序的沙箱部分中使用的图像。
  • CustomChromeHTML-app.xml: AIR 应用程序描述符文件。
  • AIR 图标文件范例

必备知识

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

镶边的类型

Custom Chrome 范例使用四种常用类型的窗口镶边:

  • 按钮: 具有样式的锚记标签, 其中以图像作为关闭、最大化、最小化和还原窗口所使用的按钮。
  • 移动手柄: 具有样式的 div 标签, 其中以背景图像和鼠标按下事件作为“拖动条”, 用于在桌面上四处移动窗口。
  • 调整大小手柄: 具有样式的 div 标签, 其中含有背景图像和鼠标按下事件, 用于调整窗口大小。Custom Chrome 范例只实现了从右下角调整大小, 但 AIR 支持从窗口的每个边角调整大小。
  • 背景: 使用 div 标签绘制不规则背景。对背景使用透明的 PNG 图形, 从而实现彩色的透明。
  • 工作区: 由包含 iframe 的 div 标签定义。工作区与背景的一部分有所重叠。将示例内容 (映射至远程沙箱) 加载至 iframe 中。

图 2 以分解形式查看 Custom Chrome 应用程序, 单独显示各种镶边元素。

Custom chrome 窗口

图 2。自定义镶边的分解视图。

了解代码

若要为 AIR 中不使用系统镶边的窗口编写代码, 请将 <systemChrome>false</systemChrome> 添加至应用程序描述符文件的 <initialWindow> 元素。若要使用户可以移动窗口、调整窗口大小或更改窗口的显示状态, 随后必须向窗口添加自己的镶边。此示例演示如何使用标准的 HTML、CSS 和 JavaScript 添加自己的镶边, 以及如何使用 AIR NativeWindow 函数从自定义镶边中控制窗口。

定义应用程序结构

Custom Chrome 应用程序的结构分为两部分。第一部分定义窗口镶边, 并包含调用 AIR API 移动窗口、调整窗口大小、关闭窗口和更改窗口显示状态的代码。第二部分实质上是窗口的“工作区”, 并定义严格意义上的应用程序。在 Custom Chrome 中, 此应用程序是使用 Canvas API 绘制的简单动画。

第一部分 (在 html/index.html 中定义) 使用 iFrame 加载第二部分 sandbox/html/child.html:

<iframe id="application" src="html/child.html" 
		    sandboxRoot="http://localhost/" 
		    documentRoot="app:/sandbox/" 
		    frameBorder="0"  width="100%" height="100%"
		    onDOMInitialize="setBridge()">
</iframe>

这种二元结构使应用程序的主要部分可以轻松地映射至相应的沙箱, 而不会妨碍镶边元素的操作。例如, 如果应用程序显示来自远程服务器的地图, 并且页面中包括其自身用于控制地图的 JavaScript 函数, 则可以将 sandboxRoot 属性更改为远程服务器所在的域。另一方面, 如果应用程序直接调用 AIR API, 则可以完全删除 sandboxRoot 属性, 以使内容保留在应用程序沙箱中。在每种情况下都不必更改镶边的代码, 因为代码始终运行于应用程序沙箱中。

Custom Chrome 中使用的示例内容可以运行于任何一个沙箱中, 其中包括应用程序沙箱。出于演示目的, 将这些内容映射至远程沙箱 http://localhost/

设置 sandboxRoot 属性时, 请谨防遗忘 URL 架构。可以使用 http: 或 https: 架构将内容映射至远程沙箱, 还可以使用 file: 架构将内容映射至 local-with-filesystem 沙箱。尽管如此, 如果遗漏了架构, 内容也将保留在应用程序沙箱中, 而对于为什么某些只允许在应用程序沙箱之外使用的 JavaScript 函数会意外地失败, 您可能会大为不解。

创建沙箱桥

将内容映射至应用程序沙箱之外后, 这些内容就无法再调用 AIR API。有两项功能需要 Custom Chrome 应用程序从应用程序沙箱之外访问 AIR API 才能使用。第一项是 Source Viewer 功能, 由 DHTML 上下文菜单触发;第二项是 AIR trace() 函数, 它对于调试非常有用。若要使应用程序沙箱中的内容可以与非应用程序沙箱中的内容交互, 可以设置父或子沙箱桥, 反之亦然。

每座桥都是单向的。父桥允许子内容调用应用程序沙箱父文档中的函数以及访问其中的属性。这正是 Custom Chrome 中所使用桥的类型。子桥允许父文档调用子文档中的函数和访问其中的属性。在这两种情况下, 访问桥的代码都只能访问单个对象的属性和方法, 该对象被赋给 JavaScript Window 对象的 parentSandboxBridgechildSandboxBridge 属性。Custom Chrome 创建名为一个 Bridge 的对象, 并创建两个成员函数, viewSource()trace():

var Bridge = {};
Bridge.viewSource = function(){
    SourceViewer.getDefault().viewSource(); 		    	    
}
Bridge.trace = function(string){
    air.trace(string);
}

Custom Chrome 用 WindowControl.js 中定义的 setBridge() 函数设置 child.html 文档的 parentSandboxBridge 属性, 以响应 iframe 的 dominitialize 事件:

function setBridge(){
    var childApp = document.getElementById("application").contentWindow;
    childApp.parentSandboxBridge = Bridge;
}

父文档随时都可以创建桥, 但为了使子文档中的脚本可以在处理 load 事件之前访问桥, 必须使用 dominitialize 事件。

定义镶边元素

每个镶边元素都分为两个部分: HTML 元素 (将镶边元素放入 DOM 中, 并分配一个 id) 和一个或多个 CSS 样式 (指定对镶边元素要使用的位置和图像)。此外, 通过使用 onClick 属性或查找 JavaScript 中的元素 id, 向按钮或拖动手柄二者之一添加事件侦听器。

CSS position:fixed 样式使相对于窗口边框确定镶边元素的位置变得很简便。可以使用 topbottomleftright 属性将镶边元素的边缘“粘”在距窗口边缘指定距离的地方。然后, 在调整窗口大小时, 镶边元素自动将自身重新定位, 如有必要, 还会重新调整大小。例如, 以下样式用于背景的右上部分:

.background {
    position:fixed; 
    background-image:url("../images/background.png");
    }
#backgroundRight {left:99; right:25; top:44; height:75;}

由于设置了 leftright 属性, 因此这部分将调整水平大小。由于将 top 属性设置为 44, 将 height 设置为 75, 因此这部分始终与窗口顶部保持 44 像素的距离, 且高度为 75 像素。

注意: AIR 不支持 CSS opacity 属性。如果对元素设置背景颜色, 则背景将完全不透明。若要模拟彩色的透明, 可以使用所需颜色和不透明度的 PNG 图形作为元素的背景图像。

按钮镶边

在 html 主文件中使用锚记标签定义窗口按钮:

<a href="#" id="close" class="button" onClick="onClose()"></a>

onClick 事件属性将按钮附加至相关的事件处理函数。使用相应的 CSS 样式确定 anchor 元素的位置, 并为其提供图像:

.button {
    position:fixed;
}
a#close {
    top:7px; right:40px; width:51px; height:41px;
    background:url("../images/buttons/close_over.png")
}
a#close:link {background:url("../images/buttons/close_up.png")}
a#close:visited {background:url("../images/buttons/close_up.png")}
a#close:hover {background:url("../images/buttons/close_over.png")}
a#close:active {background:url("../images/buttons/close_down.png")}

移动手柄和调整大小手柄镶边

在 html 主文件中用 div 元素定义移动手柄和调整大小手柄:

<div id="topDragBar"></div>
    <div id="bottomGroup">
    <div id="bottomDragBar"></div>
    <div id="resizeGripper"></div>
</div>

使用 CSS 样式放置这些手柄。例如, 用以下选择器为用于移动窗口的顶部栏设置样式:

#topDragBar {
    position:fixed; right:0; top:0; width:211; height:157;
    background:url(../images/handles/topHandle.png);
}

initialize() 函数中的每个元素添加 mouseDown 事件处理函数:

var topBar = document.getElementById("topDragBar");
topBar.addEventListener("mousedown",onMove,true);

允许取消窗口操作

当窗口使用系统镶边时, 通过侦听并取消相关事件的默认行为, 可以取消用户与窗口的交互。例如, 当用户单击系统镶边的关闭按钮时, 将调度 closing 事件。如果任何已注册的侦听器调用事件的 preventDefault() 方法, 则由于其包含未保存的数据, 可能不会关闭窗口。

窗口不使用系统镶边时, 在做出变化之前不会自动调度预期变化的通知事件。因此, 如果调用关闭窗口、更改窗口状态的方法, 或设置任何窗口边框属性, 则无法取消这种变化。

在 AIR 中, 大多数对象都可以调度事件, 其中包括 NativeWindow 对象。为了在窗口真正发生变化之前通知应用程序中的组件, 应用程序逻辑可以使用原有窗口的 dispatchEvent() 方法调度相应的通知事件。例如, 以下逻辑为窗口的关闭按钮实现一个可取消的事件处理函数:

function onClose(){
    var closing = new air.Event(air.Event.CLOSING, true, true);
    window.nativeWindow.dispatchEvent(closing);
    if(!closing.isDefaultPrevented()){
        nativeWindow.close();
    }
}

注意: 虽然侦听器调用事件的 preventDefault() 方法时 dispatchEvent() 方法返回 false, 但也有可能由于其它原因返回 false。最好显式使用 isDefaultPrevented() 方法测试是否应该取消窗口变化。

初始化窗口

为了初始化窗口, Custom Chrome 调用 initialize() 函数, 以响应由 body 元素调度的 load 事件:

function initialize(){
    var topBar = document.getElementById("topDragBar");
    topBar.addEventListener("mousedown",onMove,true);
    var bottomBar = document.getElementById("bottomDragBar");
    bottomBar.addEventListener("mousedown",onMove,true);
    var resizeGripper = document.getElementById("resizeGripper");
    resizeGripper.addEventListener("mousedown",onResize,true);
    maxButton = document.getElementById("maximize");
    restoreButton = document.getElementById("restore");
    nativeWindow.addEventListener(air.NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGE, onDisplayStateChange);
}

该函数按 id 查找镶边元素, 以便可以在控制代码中引用这些元素, 此外该函数还添加事件侦听器。即使不显示系统镶边的标题栏和按钮, 用户也仍然可以通过操作系统以其它方法 (如 Windows 中的 Alt+Space 菜单) 更改、最小化、最大化和还原窗口。因此, Custom Chrome 通过使用 JavaScript Window 对象的 nativeWindow 属性向窗口添加事件侦听器, 从而侦听窗口显示状态的变化。此属性实际上是与桌面窗口对象的接口, 因此 AIR 自动向应用程序沙箱内运行的所有页面添加此属性。

调整窗口大小和移动窗口

调用窗口的 startResize()startMove() 方法将发起系统促成的窗口变化。使用用户的鼠标或键盘操作更改窗口边框。在本例中, 每一步变化之前都自动生成 moving 或 resizing 事件。如果有任何侦听器取消这些事件, 则终止移动或调整大小的动作序列, 且不会对窗口做出进一步变化。

Custom Chrome 使用以下函数发起移动和调整大小的动作:

var onMove = function(event){
    nativeWindow.startMove();
}
var onResize = function(event){
    nativeWindow.startResize(air.NativeWindowResize.BOTTOM_RIGHT);
}

传递至 startResize() 方法的参数确定如何进行调整大小操作, 以及应该如何与引发调整大小操作的镶边元素的位置相匹配。由于调整大小手柄位于窗口的右下角, 因此调用函数时使用 bottomRight 调整大小类型。对于该参数所允许取的值的常量由 NativeWindowResize 类定义, 并对应于窗口的每个边角。

控制窗口显示状态

可以使用 NativeWindow 对象的 maximize()minimize()restore() 方法控制窗口显示状态。直接调用这些方法中的某个时, 不会调度 displayStateChanging 事件, 因此为了允许其它组件为变化做好准备, Custom Chrome 自身将调度该事件。例如, 按如下所示定义用于使窗口最大化的函数:

function onMaximize(){
    var maximizing = new air.NativeWindowDisplayStateEvent(
                        air.NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGING,
                        true, true,
                        window.nativeWindow.displayState,
                        air.NativeWindowDisplayState.MAXIMIZED);
    window.nativeWindow.dispatchEvent(maximizing);
    if(!maximizing.isDefaultPrevented()){
        nativeWindow.maximize();
    }
}

关闭窗口

Custom Chrome 使用 NativeWindow 对象的 close() 方法关闭窗口。直接调用 close 方法不会自动生成可取消的 closing 事件, 因此最好在调用 close() 之前调度 closing 事件。这样在 (例如) 仍有数据要保存的情况下, 应用程序的其它组件可以为关闭操作做好准备, 甚至否决关闭操作:

function onClose(){
    var closing = new air.Event(air.Event.CLOSING, true, true);
    window.nativeWindow.dispatchEvent(closing);
    if(!closing.isDefaultPrevented()){
        window.close();
    }
}

JavaScript Window 对象和 AIR NativeWindow 对象之间在所提供的窗口控制函数方面有所重叠。例如, 既可以用 window.close(), 也可以用 window.nativeWindow.close() 关闭窗口。如有这种重叠现象, 请使用您认为最方便的方法。

关于作者

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