辅助功能*

Charles Ward

Charles Ward

Adobe

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

自定义窗口的外观

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

Custom chrome 窗口

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

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

要求

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

Adobe AIR

Adobe Flex Builder 3

范例文件:

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

  • CustomChrome.as: 应用程序主文件 (ActionScript 格式)。
  • chrome/Chrome.as: 根据当前窗口大小定义排列镶边元素所使用的属性和函数。
  • chrome/Background.as: 扩展 Chrome 类, 用于绘制窗口背景。
  • chrome/ContentArea.as: 扩展 Chrome 类, 用于定义窗口中的主要绘制区域。
  • buttons/CloseButton.as: 扩展 Chrome 类, 用于实现关闭按钮。
  • buttons/MaxButton.as: 扩展 Chrome 类, 用于实现最大化按钮。
  • buttons/MinButton.as: 扩展 Chrome 类, 用于实现最小化按钮。
  • buttons/RestoreButton.as: 扩展 Chrome 类, 用于实现还原按钮。
  • buttons/gfx/*.png: 用作按钮的图像
  • handles/TopChrome.as: 扩展 Chrome 类, 用于实现顶部的拖动手柄。
  • handles/BottomChrome.as: 扩展 Chrome 类, 用于实现底部的拖动手柄。
  • handles/GripperChrome.as: 扩展 Chrome 类, 用于实现调整大小手柄。
  • handles/gfx/*.png: 用作手柄的图像。
  • application/LissajousFigure.as: 根据窗口尺寸进行变化的一个简单动画。
  • CustomChrome-app.xml: AIR 应用程序描述符文件。
  • AIR 图标文件范例

必备知识

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

镶边的类型

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

  • 按钮: SimpleButton 对象和鼠标单击事件, 用于关闭、最大化、最小化和还原窗口。
  • 移动手柄: 基于位图的子画面以及鼠标按下事件, 用于实现窗口的移动。
  • 调整大小手柄: 基于位图的子画面以及鼠标按下事件, 用于实现对窗口大小的调整。Custom Chrome 范例只实现了从右下角调整大小, 但 AIR 支持从窗口的每个边角调整大小。
  • 背景: 使用 Sprite 对象和矢量绘画命令绘制不规则的背景。

安装和测试应用程序

我们特意将 Custom Chrome 应用程序设计得很简单。旨在介绍有关如何实现自定义窗口镶边的基础知识。

若要测试应用程序, 请下载并运行应用程序安装程序 (CustomChrome.air)。可以最大化、最小化和还原窗口, 在桌面上四处拖动窗口, 以及调整窗口大小。在窗口中单击右键可以在新窗口中打开源代码浏览器。

了解代码

若要在 AIR 中用代码创建不使用系统镶边的窗口, 可以使用 Flex 镶边以及 mx:Window 和 mx:WindowedApplication 组件, 也可以用代码创建自己的镶边。本范例演示如何使用 Flash API 绘制位图和矢量图形, 从而添加您自己的镶边。对于此应用程序所使用的全部 ActionScript 类, 本文将不一一介绍。有关这些类的信息, 请参阅《Flex 3 语言参考》*

初始化窗口

CustomChrome 类扩展 Sprite 类, 以使后者可以用作 SWF 应用程序文件中的根类。AIR 自动创建窗口, 对 CustomChrome 类的实例进行实例化, 并将 CustomChrome 对象添加至窗口舞台。

CustomChrome 构造函数添加一个事件侦听器, 用于检测何时将该对象添加至舞台。addedToStage 事件的处理函数使用舞台的 nativeWindow 属性获取窗口实例, 并初始化窗口属性。

注意: 您可能会注意到可以直接从类的构造函数访问舞台, 而不必等待 addedToStage 事件。但是, 这种习惯可能并不好, 因为只有对 AIR 应用程序初始窗口中的 Sprite 主类才是这样。

初始化任务包括:

  • 添加针对基本窗口事件的事件侦听器:

    win.addEventListener(NativeWindowBoundsEvent.RESIZE,onBoundsChange);
    win.addEventListener(NativeWindowBoundsEvent.MOVE,onBoundsChange);
    win.addEventListener(NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGE,
    onDisplayStateChange);

    Custom Chrome 使用这些事件在调整窗口大小时重新定位和重新绘制窗口镶边元素, 并控制对于特定窗口显示状态要显示哪些元素。

  • 设置舞台属性:

    stage.align = StageAlign.TOP_LEFT;
    stage.scaleMode = StageScaleMode.NO_SCALE;

    将舞台的对齐方式设置为 topLeft 表示将添加至窗口的所有子画面的 x 和 y 坐标都初始化为 (0,0)。将舞台的缩放模式设置为 noScale 可以防止舞台在调整窗口大小时进行缩放。由于舞台不能缩放, 因此将窗口调小时会剪裁窗口的内容, 除非重新定位和重新绘制这些内容。

  • 添加镶边:

    addChildAt(background,0);
    addChildAt(topChrome,1);
    addChildAt(closeButton,2);
    addChildAt(minimizeButton,2);
    addChildAt(maximizeButton,2);
    addChildAt(restoreButton,2);
    bottomGroup.addChild(bottomChrome);
    bottomGroup.addChild(gripperChrome);
    addChildAt(bottomGroup,1);

    使用 addChildAt() 方法显式设置用于镶边元素的子画面的深度排序。 (如果使用 addChild(), 则按将子画面添加至其容器的顺序显式设置深度。) 由于在使窗口最大化时 Custom Chrome 隐藏 bottomChromegripperChrome 子画面, 因此这些子画面被集中在同一个容器 bottomGroup 中, 这样用一个 bottomGroup.visible 属性即可控制这些子画面的可见性。

定义镶边元素

Custom Chrome 定义 Chrome 类作为所有镶边元素的基类。

Chrome 类的构造函数用一个矩形定义镶边的定位和大小, 用一个字符串定义如何将该控件停靠至窗口边框, 并用一个可选的函数引用将镶边元素附加至其相关控制函数。例如, 用以下声明定义关闭按钮:

private var closeButton:CloseButton =
    new CloseButton(new Rectangle(65,8,0,0), 'TR', onCloseCommand)

根据 anchorType 参数将控件停靠至窗口边框。“TR”值指定将控件停靠至右上角。

按钮镶边

为了实现用于控制窗口状态的按钮, Custom Chrome 扩展 Chrome 类, 并添加 SimpleButton 对象作为镶边元素的子对象。按钮状态的图像由 button chrome 类加载。每个 button chrome 类还添加事件侦听器, 并将 offsetanchorType 参数传递至 Chrome 超类。

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

为了实现移动手柄和调整大小手柄, Custom Chrome 扩展 Chrome 基类, 并添加 chrome image 作为子类。使用 PNG 文件作为图像, 因此可以定义透明区域。在图像的透明区域中不捕获鼠标事件。

背景

为了绘制背景, Custom Chrome 扩展 Chrome 基类, 并添加 draw() 方法用于绘制矢量图。为了调用 draw() 方法, Background 类覆盖基类的 layout() 方法。

允许取消窗口操作

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

当窗口不使用系统镶边时, 不会自动调度调整大小或关闭等窗口变化的通知事件。因此, 如果调用关闭窗口、更改窗口显示状态的方法, 或设置任何窗口边框属性, 则无法取消这种变化。为了在真正发生窗口变化之前通知应用程序中的组件, 应用程序逻辑可以使用窗口的 dispatchEvent() 方法调度相应的通知事件。例如, 以下逻辑为窗口的关闭按钮实现一个可取消的事件处理函数:

public function onCloseCommand(event:MouseEvent):void{
    var closing:Event = new Event(Event.CLOSING,true,true);
    dispatchEvent(closing);
    if(!closing.isDefaultPrevented()){
        win.close();
    }
}

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

调整窗口大小和移动窗口

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

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

public function onMoveCommand(event:MouseEvent):void{
    win.startMove();
}
public function onResizeCommand(event:MouseEvent):void{
    win.startResize(NativeWindowResize.BOTTOM_RIGHT);
}

移动窗口时, 其内容随之一起移动 (正如您所预料), 但调整窗口大小时, 情况要复杂一些。窗口的默认行为取决于舞台的缩放模式。如果缩放模式是除 noScale 之外的任何模式, 则调整窗口大小时将缩放舞台及其内容。这意味着对添加至窗口的任何按钮或其它镶边也要进行缩放, 而这可能并非所希望达到的效果。另一方面, 当使用 noScale 模式时将不缩放镶边, 但随后也不会沿调整大小后的窗口边框移动镶边。如果将窗口调小, 则可能会剪裁镶边。如果从顶边将窗口调大, 则可能会将标题栏留在中间。

CustomChrome 范例解决了这一问题, 方法是相对于窗口边角绘制其镶边, 并且每当窗口调度 resize 事件时都重新绘制。每个镶边元素通过由 Chrome 基类添加的事件侦听器来侦听 resize 事件:

stage.nativeWindow.addEventListener(NativeWindowBoundsEvent.RESIZE, onWindowResize);

收到事件后, 镶边元素根据事件对象的 afterBounds 属性设置该元素的位置和大小:

private function onWindowResize(boundsEvent:NativeWindowBoundsEvent):void{
    layout(boundsEvent.afterBounds);
}
 
public function layout(bounds:Rectangle):void{
    setPosition(bounds);
    if(resizable){
        setSize(bounds);
    }
}

注意: 如果正在使用 Flex 组件, 则框架的自动布局功能已解决了此问题。

控制窗口显示状态

可以使用 NativeWindow 对象的 maximize()minimize()restore() 方法控制窗口显示状态。为了使其它组件可以对变化做好准备, Custom Chrome 在更改显示状态之前调度一个 displayStateChanging 事件:

public function onMaximizeCommand(event:MouseEvent):void{
    var displayStateChanging:NativeWindowDisplayStateEvent =
    new NativeWindowDisplayStateEvent(
    NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGING, true, true);
    dispatchEvent(displayStateChanging);
    if(!displayStateChanging.isDefaultPrevented()){
        win.maximize();
    }
}

用于更改显示状态的命令与更新镶边元素的大小和位置的过程相互分离。Custom Chrome 侦听 displayStateChange 事件来检测显示状态的变化, 并且侦听 resize 事件来检测窗口大小的变化。

关闭窗口

CustomChrome 使用 NativeWindow 对象的 close() 方法关闭窗口。为了使其它组件可以对变化做好准备, Custom Chrome 在关闭窗口之前调度一个 closing 事件:

public function onCloseCommand(event:MouseEvent):void{
    var closing:Event = new Event(Event.CLOSING,true,true);
    dispatchEvent(closing);
    if(!closing.isDefaultPrevented()){
        win.close();
    }
}

以 MXML 应用程序的形式创建 Custom Chrome

尽管 Custom Chrome 完全是以 ActionScript 编写而成, 但使用 Flex 框架也可以实现相同的结果。为此, 要为主窗口定义 mx:WindowedApplication 组件, 并具有以下属性:

<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"		    
layout="absolute" 
showFlexChrome="false">

然后可以定义镶边元素的 MXML 组件。Flex 框架使排列组件变得容易很多, 因此可以自动处理许多问题, 其中涉及跟踪窗口 resizing 事件以及重新定位或重新绘制镶边元素。例如, 不必存储按钮的锚点值, 可以使用 Flex 定义的布局属性 right 和 top 相对于窗口边框放置按钮:

<mx:Button label="Button" right="10" top="10"/>

注意: 无法添加 Sprite 对象作为 Flex 组件的子对象。必须将 Sprite 的类更改为 UIComponent (其自身扩展 Sprite), 或者添加 Sprite 作为 UIComponent 对象的子对象, 并添加 UIComponent 实例作为 Flex 组件的子组件。

关于作者

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