PS入门教程-ActionScript 入门教程

一 : ActionScript 入门教程

ActionScript 入门教程

描述
第 1-4 章,ActionScript 编程概述
讨论 ActionScript 3.0 核心概念,其中包括语言语法、语句和运算符、ECMAScript 第4 版语言规范草案、面向对象的 ActionScript 编程以及管理 Adobe® Flash® Player 9 显示列表中的显示对象的新方法。
第 5-10 章,ActionScript 3.0 核心数据类型和类
介绍 ActionScript 3.0 中的顶级数据类型(也是 ECMAScript 规范草案的一部分)。
第 11-26 章,Flash Player API
介绍在特定于 Adobe Flash Player 9 的包和类中实现的重要功能,其中包括事件处理、网络和通信、文件输入和输出、外部接口、应用程序安全模型等。

ActionScript 快速入门

关于 ActionScript
ActionScript 是由 Flash Player 中的 ActionScript 虚拟机 (AVM) 来执行的。ActionScript 代码通常被编译器编译成"字节码格式"(一种由计算机编写且能够为计算机所理解的编程语言),如 Adobe Flash CS3 Professional 或 Adobe® Flex™ Builder™ 的内置编译器或 Adobe® Flex™ SDK 和 Flex™ Data Services 中提供的编译器。字节码嵌入 SWF 文件中,SWF 文件由运行时环境 Flash Player 执行。
ActionScript 3.0 中的一些主要功能包括:
一个新增的 ActionScript 虚拟机,称为 AVM2,它使用全新的字节码指令集,可使性能显著提高。
一个更为先进的编译器代码库,它更为严格地遵循 ECMAScript (ECMA 262) 标准,并且相对于早期的编译器版本,可执行更深入的优化。
一个扩展并改进的应用程序编程接口 (API),拥有对对象的低级控制和真正意义上的面向对象的模型。
一种基于即将发布的 ECMAScript (ECMA-262) 第 4 版草案语言规范的核心语言。
一个基于 ECMAScript for XML (E4X) 规范(ECMA-357 第 2 版)的 XML API。E4X 是 ECMAScript 的一种语言扩展,它将 XML 添加为语言的本机数据类型。
一个基于文档对象模型 (DOM) 第 3 级事件规范的事件模型。

编程基础
计算机程序主要包括两个方面:一、程序是计算机执行的一系列指令或步骤。 二、每一步最终都涉及到对某一段信息或数据的处理。 通常认为,计算机程序只是您提供给计算机并让它逐步执行的指令列表。每个单独的指令都称为"语句"。在 ActionScript 中编写的每个语句的末尾都有一个分号。实质上,程序中给定指令所做的全部操作就是处理存储在计算机内存中的一些数据位。

变量和常量
"变量"是一个名称,它代表计算机内存中的值。在编写语句来处理值时,编写变量名来代替值;只要计算机看到程序中的变量名,就会查看自己的内存并使用在内存中找到的值。
在 ActionScript 3.0 中,一个变量实际上包含三个不同部分:
变量名 计算机将名称用作值的占位符。
可以存储在变量中的数据的类型
存储在计算机内存中的实际值
在 ActionScript 中,要创建一个变量(称为"声明"变量),应使用 var 语句。
在 Adobe Flash CS3 Professional 中,还包含另外一种变量声明方法。在将一个影片剪辑元件、按钮元件或文本字段放置在舞台上时,可以在"属性"检查器中为它指定一个实例名称。在后台,Flash 将创建一个与该实例名称同名的变量,您可以在 ActionScript 代码中使用该变量来引用该舞台项目。



数据类型
在 ActionScript 中,您可以将很多数据类型用作所创建的变量的数据类型。其中的某些数据类型可以看作是"简单"或"基本"数据类型:
String:一个文本值
Numeric:对于 numeric 型数据,ActionScript 3.0 包含三种特定的数据类型:
Number:任何数值,包括有小数部分或没有小数部分的值
Int:一个整数(不带小数部分的整数)
Uint:一个"无符号"整数,即不能为负数的整数
Boolean:一个 true 或 false 值
简单数据类型表示单条信息。然而,ActionScript 中定义的大部分数据类型都可以被描述为复杂数据类型,因为它们表示组合在一起的一组值。
大部分内置数据类型以及程序员定义的数据类型都是复杂数据类型。您可能认识下面的一些复杂数据类型:
MovieClip:影片剪辑元件
TextField:动态文本字段或输入文本字段
SimpleButton:按钮元件
Date:有关时间中的某个片刻的信息(日期和时间)
经常用作数据类型的同义词的两个词是类和对象。"类"仅仅是数据类型的定义 ─ 就像用于该数据类型的所有对象的模板,而"对象"仅仅是类的一个实际的实例。下面几条陈述虽然表达的方式不同,但意思是相同的:
变量 myVariable 的数据类型是 Number。
变量 myVariable 是一个 Number 实例。
变量 myVariable 是一个 Number 对象。
变量 myVariable 是 Number 类的一个实例。

处理对象
在面向对象的编程中,程序指令被划分到不同的对象中 ─ 代码构成功能块,因此相关类型的功能或相关的信息被组合到一个容器(即 类)中。
在 ActionScript 面向对象的编程中,任何类都可以包含三种类型的特性:属性、 方法 、事件。
属性 表示某个对象中绑定在一起的若干数据块中的一个。您可以像处理单个变量那样处理属性;事实上,可以将属性视为包含在对象中的"子"变量。
将变量用作对象的名称,后跟一个句点 (.) 和属性名。句点称为"点运算符",用于指示您要访问对象的某个子元素。整个结构"变量名-点-属性名"的使用类似于单个变量,变量是计算机内存中的单个值的名称。
方法 是指可以由对象执行的操作。可以通过依次写下对象名(变量)、句点、方法名和小括号来访问方法,这与属性类似。小括号是指示要"调用"某个方法的方式。有时,为了传递执行动作所需的额外信息,将值(或变量)放入小括号中。这些值称为方法"参数"。
与属性(和变量)不同的是,方法不能用作值占位符。然而,一些方法可以执行计算并返回可以像变量一样使用的结果。
事件 事件是确定计算机执行哪些指令以及何时执行的机制。本质上,"事件"就是所发生的、ActionScript 能够识别并可响应的事情。
基本事件处理
指定为响应特定事件而应执行的某些动作的技术称为"事件处理"。
在编写执行事件处理的 ActionScript 代码时,您需要识别三个重要元素:
事件源:发生该事件的是哪个对象?事件源也称为"事件目标",因为 Flash Player 将此
对象(实际在其中发生事件)作为事件的目标。
事件:将要发生什么事情,以及您希望响应什么事情?识别事件是非常重要的,因为许多
对象都会触发多个事件。
响应:当事件发生时,您希望执行哪些步骤?
无论何时编写处理事件的 ActionScript 代码,都会包括这三个元素,并且代码将遵循以下基本结构(以粗体显示的元素是您将针对具体情况填写的占位符):
function eventResponse(eventObject:EventType):void
{
// 此处是为响应事件而执行的动作。
}
eventSource.addEventListener(EventType.EVENT_NAME, eventResponse);
此代码执行两个操作。首先,定义一个函数,这是指定为响应事件而要执行的动作的方法。接下来,调用源对象的 addEventListener() 方法,实际上就是为指定事件"订阅"该函数,以便当该事件发生时,执行该函数的动作。
"函数"提供一种将若干个动作组合在一起、用类似于快捷名称的单个名称来执行这些动作的方法。函数与方法完全相同,只是不必与特定类关联(事实上,方法可以被定义为与特定类关联的函数)。在创建事件处理函数时,必须选择函数名称(本例中为 eventResponse),还必须指定一个参数(本例中的名称为 eventObject)。指定函数参数类似于声明变量,所以还必须指明参数的数据类型。将为每个事件定义一个 ActionScript 类,并且为函数参数指定的数据类型始终是与要响应的特定事件关联的类。(即 此处的这个类的事件特性中必须包含要您希望要响应的事件。)最后,在左大括号与右大括号之间 ({ ...}),编写您希望计算机在事件发生时执行的指令。
一旦编写了事件处理函数,就需要通知事件源对象(发生事件的对象)您希望在该事件发生时调用函数。可通过调用该对象的 addEventListener() 方法来实现此目的(所有具有事件的对象都同时具有 addEventListener() 方法)。addEventListener() 方法有两个参数:
第一个参数是您希望响应的特定事件的名称。同样,每个事件都与一个特定类关联,而该类将为每个事件预定义一个特殊值;类似于事件自己的唯一名称(应将其用于第一个参数)。
第二个参数是事件响应函数的名称。请注意,如果将函数名称作为参数进行传递,则在写入函数名称时不使用括号。
了解事件处理过程
下面分步描述了创建事件侦听器时执行的过程。在本例中,您将创建一个侦听器函数,在单击名为 myButton 的对象时将调用该函数。
程序员实际编写的代码如下所示:
function eventResponse(event:MouseEvent):void
{
// Actions performed in response to the event go here.
}
myButton.addEventListener(MouseEvent.CLICK, eventResponse);
下面是此代码在 Flash Player 中运行时的实际工作方式:
1.加载 SWF 文件时,Flash Player 会注意到以下情况:有一个名为 eventResponse() 的函数。

2.Flash Player 随后运行该代码(具体地说,是指不在函数中的代码行)。在本例中,只有一行代码:针对事件源对象(名为 myButton)调用 addEventListener() 方法,并将 eventResponse 函数作为参数进行传递。

1.在内部,myButton 包含正在侦听其每个事件的函数的列表,因此,当调用其 addEventListener() 方法时,myButton 将 eventResponse() 函数存储在其事件侦听器列表中。

3.在某一时刻,用户单击 myButton 对象以触发其 click 事件(在代码中将其标识为 MouseEvent.CLICK)。

此时发生了以下事件:
1.Flash Player 创建一个对象,它是与所述事件(本示例中为 MouseEvent)关联的类的实例。对于很多事件,这是 Event 类的实例;对于鼠标事件,这是 MouseEvent 实例;对于其它事件,这是与该事件关联的类的实例。创建的该对象称为"事件对象",它包含有关所发生的事件的特定信息:事件类型、发生位置以及其它特定于事件的信息(如果适用)。

2.Flash Player 随后查看 myButton 存储的事件侦听器的列表。它逐个查看这些函数,以调用每个函数并将事件对象作为参数传递给该函数。由于 eventResponse() 函数是 myButton 的侦听器之一,因此,Flash Player 将在此过程中调用 eventResponse() 函数。

3.当调用 eventResponse() 函数时,将运行该函数中的代码,因此,将执行您指定的动作。

创建对象实例
在 ActionScript 中使用对象之前,该对象首先必须存在。创建对象的步骤之一是声明变量;然而,声明变量仅仅是在计算机的内存中创建一个空位置。您必须为变量指定实际值 ─ 即创建一个对象并将它存储在该变量中 ─ 然后再尝试使用或处理该变量。创建对象的过程称为对象"实例化";也就是说,创建特定类的实例。
有一种创建对象实例的简单方法完全不必涉及 ActionScript。在 Flash 中,当将一个影片剪辑元件、按钮元件或文本字段放置在舞台上,并在"属性"检查器中为它指定实例名时,Flash 会自动声明一个拥有该实例名的变量、创建一个对象实例并将该对象存储在该变量中。同样,在 Adobe Flex Builder 中,当您以 Adobe Macromedia® MXML™ 创建一个组件(通过用 MXML 标签进行编码或通过将组件放置在处于设计模式下的编辑器中)并为该组件分配一个 ID(在 MXML 标记中或在 Flex 属性视图中)时,该 ID 将成为一个 ActionScript 变量的名称,并且会创建该组件的一个实例并将它存储在该变量中。 (直观地创建对象。)
通过几种方法来仅使用 ActionScript 创建对象实例。
首先,借助几个 ActionScript 数据类型,可以使用"文本表达式"(直接写入 ActionScript 代码的值)创建一个实例。ActionScript 为Number 、int 、uint 、String 、Boolean 、Array、RegExp、XML 、Object 和 Function 数据类型定义了文本表达式。
对于其它任何数据类型而言,要创建一个对象实例,应将 new 运算符与类名一起使用,通常,将使用 new 运算符创建对象称为"调用类的构造函数"。"构造函数"是一种特殊方法,在创建类实例的过程中将调用该方法。请注意,当以此方法创建实例时,请在类名后加上小括号,有时还可以指定参数值 ─ 这是在调用方法时另外可执行的两个操作。
甚至对于可使用文本表达式创建实例的数据类型,也可以使用 new 运算符来创建对象实例。
如果需要创建无可视化表示形式的 ActionScript 数据类型的一个实例(无法通过将项目放置在 Flash 舞台上来创建,也无法在Flex Builder MXML 编辑器的设计模式下创建),则只能通过使用 new 运算符在ActionScript 中直接创建对象来实现此目的。
具体到 Flash 中,new 运算符还可用于创建已在库中定义、但没有放在舞台上的影片剪辑元件的实例。




常用编程元素
运算符 是用于执行计算的特殊符号(有时候是词)。

注释 "代码注释"是一个工具,用于编写计算机应在代码中忽略的文本。ActionScript 包括两种注释:
单行注释:在一行中的任意位置放置两个斜杠来指定单行注释。计算机将忽略斜杠后直到该行末尾的所有内容。
多行注释:多行注释包括一个开始注释标记 (/*)、注释内容和一个结束注释标记 (*/)。无论注释跨多少行,计算机都将忽略开始标记与结束标记之间的所有内容。
注释的另一种常见用法是临时禁用一行或多行代码。
流控制 就是用于控制执行哪些动作。ActionScript 中提供了几种类型的流控制元素。
函数:函数类似于快捷方式,提供了一种将一系列动作组合到单个名称下的方法,并可用于执行计
算。函数对于处理事件尤为重要,但也可用作组合一系列指令的通用工具。
循环:使用循环结构,可指定计算机反复执行一组指令,直到达到设定的次数或某些条件改变为止。
通常借助循环并使用一个其值在计算机每执行完一次循环后就改变的变量来处理几个相关项。
条件语句:条件语句提供一种方法,用于指定仅在某些情况下才执行的某些指令或针对不同的条件
提供不同的指令集。最常见的一类条件语句是 if 语句。if 语句检查该语句括号中的值或表达式。如果值为 true,则执行大括号中的代码行;否则,将忽略它们。
使用 ActionScript 构建应用程序
您可以使用 ActionScript 3.0 代码来实现任何目的,从简单的图形动画到复杂的客户端 ─ 服务器事务处理系统都可以通过它来实现。
将代码存储在 Flash 时间轴中的帧中
在 Flash 创作环境中,可以向时间轴中的任何帧添加 ActionScript 代码。该代码将在影片播放期间播放头进入该帧时执行。构建较大的应用程序时,这会容易导致无法跟踪哪些帧包含哪些脚本,应用程序越来越难以维护。
许多开发人员将代码仅仅放在时间轴的第 1 帧中,或放在 Flash 文档中的特定图层上,以简化在 Flash 创作工具中组织其 ActionScript 代码的工作。这样,就可以容易地在 Flash FLA 文件中查找和维护代码。
如果想要以后能够在其它 Flash 项目中使用您的 ActionScript 代码,您需要将代码存储在外部 ActionScript 文件(扩展名为 .as 的文本文件)中。
将代码存储在 ActionScript 文件中
可以采用以下两种方式之一来设置 ActionScript 文件的结构:
非结构化 ActionScript 代码:编写 ActionScript 代码行(包括语句或函数定义),就好像它们是直接在时间轴脚本、MXML 文件等文件中输入的一样。
使用 ActionScript 中的 include 语句或 Adobe Flex MXML 中的 <mx:Script> 标签,可以访问以此方式编写的 ActionScript。ActionScript include 语句会导致在特定位置以及脚本中的指定范围内插入外部 ActionScript 文件的内容,就好像它们是直接在那里输入的一样。在 Flex MXML 语言中,可使用 <mx:Script> 标签来指定源属性,从而标识要在应用程序中的该点处加载的外部 ActionScript 文件。
ActionScript 类定义:定义一个 ActionScript 类,包含它的方法和属性。
定义一个类后,您就可以像对任何内置的 ActionScript 类所做的那样,通过创建该类的一个实例并使用它的属性、方法和事件来访问该类中的 ActionScript 代码。这要求做到下面两点:
使用 import语句来指定该类的全名,以便ActionScript 编译器知道可以在哪里找到它。
此规则的唯一例外是,如果在代码中引用的类为顶级类,没有在包中定义,则必须导入该类。
在 Flash 中,将自动为附加到时间轴上的帧的脚本导入内置类(在 flash.* 包中)。但是,如果您编写自己的类、处理 Flash 创作组件(fl.* 包)或在 Flex 中工作,则需要显式地导入任何类以编写用于创建该类实例的代码。
编写明确引用类名的代码(通常声明一个用该类作为其数据类型的变量,并创建该类的一个实例以便存储在该变量中)。在 ActionScript 代码中引用其它类名即通知编译器加载该类的定义。
选择合适的工具
Flash 创作工具
除了创建图形和动画的功能之外,Adobe Flash CS3 Professional 还包括处理 ActionScript 代码(附加到 FLA 文件中的元素的代码,或仅包含 ActionScript 代码的外部文件中的代码)的工具。Flash 创作工具最适合于涉及大量的动画或视频的项目,或者您希望自己创建大部分图形资源的项目,尤其适合于用户交互很少或者具有需要 ActionScript 的功能的项目。如果您希望在同一个应用程序中既创建可视资源又编写代码,也可能会选择使用 Flash 创作工具来开发 ActionScript 项目。如果您希望使用预置的用户界面组件,但 SWF 较小或便于设置可视外观是项目的主要考虑因素,那么您也可能会选择使用 Flash 创作工具。
Adobe Flash CS3 Professional 包括两个编写 ActionScript 代码的工具:
"动作"面板:在 FLA 文件中工作时可用,该面板允许您编写附加到时间轴上的帧的 ActionScript 代码。
"脚本"窗口:"脚本"窗口是专门用于处理 ActionScript (.as) 代码文件的文本编辑器。
Flex Builder
Adobe Flex Builder 是创建带有 Flex 框架的项目的首选工具。除了可视布局和 MXML 编辑工具之外,Flex Builder 还包括一个功能完备的 ActionScript 编辑器,因此可用于创建 Flex 或仅包含 ActionScript 的项目。Flex 应用程序具有以下几个优点:包含一组内容丰富的预置用户界面控件和灵活的动态布局控件,内置了用于处理外部数据源的机制,以及将外部数据链接到用户界面元素。但由于需要额外的代码来提供这些功能,因此 Flex 应用程序的 SWF 文件可能比较大,并且无法像 Flash 应用程序那样轻松地完全重设外观。
如果希望使用 Flex 创建功能完善、数据驱动且内容丰富的 Internet 应用程序,并在一个工具内编辑 ActionScript 代码,编辑 MXML 代码,直观地设置应用程序布局,则应使用 Flex Builder。
第三方 ActionScript 编辑器
由于 ActionScript (.as) 文件存储为简单的文本文件,因此任何能够编辑纯文本文件的程序都可以用来编写 ActionScript 文件。您可以使用任何文本编辑程序来编写 MXML 文件或 ActionScript 类。然后,可以使用 Flex SDK(包括 Flex 框架类和 Flex 编译器)来基于这些文件创建 SWF 应用程序(Flex 或仅包含 ActionScript 的应用程序)。或者,很多开发人员也可以使用第三方 ActionScript 编辑器来编写 ActionScript 类,并结合使用 Flash 创作工具来创建图形内容。
在以下情况下,您可以选择使用第三方 ActionScript 编辑器:
您希望在单独的程序中编写 ActionScript 代码,而在 Flash 中设计可视元素。
将某个应用程序用于非 ActionScript 编程(例如,创建 HTML 页或以其它编程语言构建应用程序),并希望将该应用程序也用于 ActionScript 编码。
您希望使用 Flex SDK 而不用 Flash 和 Flex Builder 来创建仅包含 ActionScript 的项目或 Flex 项目。
有一些提供特定于 ActionScript 的支持的代码编辑器值得注意,其中包括:Adobe Dreamweaver® CS3 、ASDT、 FDT、 FlashDevelop 、PrimalScript、 SE|PY、 Xcode(带有 ActionScript 模板和代码提示文件)。
ActionScript 开发过程
ActionScript 3.0 的应用程序的基本开发过程:
1.设计应用程序。
您应先以某种方式描述应用程序,然后再开始构建该应用程序。
2.编写 ActionScript 3.0 代码。
您可以使用 Flash、Flex Builder、Dreamweaver 或文本编辑器来创建 ActionScript 代码。
3.创建 Flash 或 Flex 应用程序文件来运行代码。
在 Flash 创作工具中,此步骤包括:创建新的 FLA 文件、设置发布设置、向应用程序添加用户界面组件以及引用 ActionScript 代码。在 Flex 开发环境中,创建新的应用程序文件涉及:定义该应用程序并使用 MXML 来添加用户界面组件以及引用 ActionScript 代码。
4.发布和测试 ActionScript 应用程序。
这涉及在 Flash 创作环境或 Flex 开发环境中运行应用程序,确保该应用程序执行您期望的所有操作。
不必按顺序执行这些步骤,或者说不必在完全完成一个步骤后再执行另一步骤。虽然记住开发过程的这 4 个阶段是十分有用的,但在实际的开发过程中适当地调整各个阶段的顺序通常有助于提高效率。
创建自己的类
类设计策略 下面还是给出了几条建议以帮助您着手进行面向对象的编程。
1.请考虑一下该类的实例将在应用程序中扮演的角色。通常,对象担任以下三种角色之一:
值对象:这些对象主要用作数据的容器 ─ 也就是说,它们可能拥有若干个属性和很少的几个方法(有时没有方法)。值对象通常是明确定义的项目的代码表示。
显示对象:它们是实际显示在屏幕上的对象。例如,用户界面元素(如下拉列表或状态显示)和图形元素(如视频游戏中的角色)等等就是显示对象。
应用程序结构:这些对象在应用程序执行的逻辑或处理方面扮演着广泛的支持角色。
2.确定类所需的特定功能。不同类型的功能通常会成为类的方法。
3.如果打算将类用作值对象,请确定实例将要包含的数据。这些项是很好的候选属性。
4.由于类是专门为项目而设计的,因此最重要的是提供应用程序所需的功能。回答下列问题可能会对
您很有帮助:
应用程序将存储、跟踪和处理哪些信息?确定这些信息有助于您识别可能需要的值对象和属性。
需要执行哪些操作 ─ 例如,在应用程序首次加载时,在单击特定的按钮时,在影片停止播放时,分别需要执行哪些操作?这些是很好的候选方法(如果"动作"仅涉及更改单个值,则是很好的候选属性)。
对于任何给定的动作,要执行该动作,该类需要了解哪些信息?这些信息将成为方法的参数。
随着应用程序开始工作,应用程序的其它部分需要了解类中的哪些内容将发生更改?这些是很好的候选事件。
5.如果有一个现有的对象与您需要的对象类似,只是缺少某些您需要添加的一些额外功能,应考虑创
建一个子类(在现有类的功能的基础之上构建的类,不需要定义它自己的所有功能)。
编写类的代码
下面是创建自己的 ActionScript 类的最基本步骤:
1.在特定于 ActionScript 的程序(如 Flex Builder 或 Flash)、通用编程工具(如 Dreamweaver)或者可用来处理纯文本文档的任何程序中打开一个新的文本文档。
2.输入 class 语句定义类的名称。为此,输入单词 public class,然后输入类名,后跟一个左大括号和一个右大括号,两个括号之间将是类的内容(方法和属性定义)。 单词 public 表示可以从任何其它代码中访问该类。
3.键入 package 语句以指示包含该类的包的名称。语法是单词 package,后跟完整的包名称,再跟左大括号和右大括号(括号之间将是 class 语句块)。
4.使用var语句,在类体内定义该类中的每个属性;语法与用于声明任何变量的语法相同(并增加了public 修饰符)。
5.使用与函数定义所用的相同语法来定义类中的每个方法。例如:
要创建普通方法。
要创建一个构造函数(在创建类实例的过程中调用的特殊方法),应创建一个名称与类名称完全匹配的方法。
如果没有在类中包括构造函数方法,编译器将自动在类中创建一个空构造函数(没有参数和语句)。
您还可以定义其它几个类元素。这些元素更为复杂。
"存取器"是方法与属性之间的一个特殊交点。在编写代码来定义类时,可以像编写方法一样来编写存取器,这样就可以执行多个动作(而不是像在定义属性时那样,只能读取值或赋值)。但是,在创建类的实例时,可将存取器视为属性 ─ 仅使用名称来读取值或赋值。
ActionScript 中的事件不是使用特定的语法来定义的。应使用 EventDispatcher 类的功能来定义类中的事件,以便跟踪事件侦听器并将事件通知给它们。
有关组织类的一些建议
Adobe 建议您始终将每个类的源代码保存在其自己的文件中,并为文件指定与类相同的名称。
ActionScript 语言及其语法
ActionScript 3.0 既包含 ActionScript 核心语言又包含 Adobe Flash Player 应用程序编程接口 (API)。ActionScript 核心语言是 ActionScript 的一部分,实现了 ECMAScript (ECMA-262) 第 4 版语言规范草案。Flash Player API 提供对 Flash Player 的编程访问。
语言概述
对象是 ActionScript 3.0 语言的核心 ─ 它们是 ActionScript 3.0 语言的基本构造块。您所声明的每个变量、所编写的每个函数以及所创建的每个类实例都是一个对象。可以将 ActionScript 3.0 程序视为一组执行任务、响应事件以及相互通信的对象。
在ECMAScript 第 4 版草案(ActionScript 3.0 所基于的标准)中,对象只是属性的集合。这些属性是一些容器,除了保存数据,还保存函数或其它对象。以这种方式附加到对象的函数称为方法。
尽管具有 Java 或 C++ 背景的程序员可能会觉得 ECMAScript 草案中的定义有些奇怪,但实际上,用 ActionScript 3.0 类定义对象类型与在 Java 或 C++ 中定义类的方式非常相似。在讨论 ActionScript 对象模型和其它高级主题时,了解这两种对象定义之间的区别很重要,但在其它大多数情况下,"属性"一词都表示类成员变量(而不是方法),使用"方法"一词表示作为类的一部分的函数。
ActionScript 中的类与 Java 或 C++ 中的类之间有一个细小的差别,那就是 ActionScript 中的类不仅仅是抽象实体。ActionScript 类由存储类的属性和方法的类对象 表示,这样便可以使用可能不为 Java 和 C++ 程序员所熟悉的方法,例如,在类或包的顶级包括语句或可执行代码。
ActionScript类与Java 或 C++ 类之间还有一个区别,那就是每个 ActionScript 类都有一个"原型对象"。在早期的 ActionScript 版本中,原型对象链接成"原型链",它们共同作为整个类继承层次结构的基础。但是,在 ActionScript 3.0 中,原型对象在继承系统中仅扮演很小的角色。但是,原型对象仍非常有用,如果您希望在类的所有实例中共享某个属性及其值,可以使用原型对象来代替静态属性和方法。
对象和类
在 ActionScript 3.0 中,每个对象都是由类定义的。可将类视为某一类对象的模板或蓝图。类定义中可以包括变量和常量以及方法,前者用于保存数据值,后者是封装绑定到类的行为的函数。存储在属性中的值可以是"基元值",也可以是其它对象。基元值是指数字、字符串或布尔值。
ActionScript 中包含许多属于核心语言的内置类。其中的某些内置类(如 Number、Boolean 和 String)表示 ActionScript 中可用的基元值。其它类(如 Array、Math 和 XML)定义属于 ECMAScript 标准的更复杂对象。
所有的类(无论是内置类还是用户定义的类)都是从 Object 类派生的。以前在 ActionScript 方面有经验的程序员一定要注意到,Object 数据类型不再是默认的数据类型,尽管其它所有类仍从它派生。在 ActionScript 2.0 中,下面的两行代码等效,因为缺乏类型注释意味着变量为 Object 类型:
var someObj:Object;
var someObj;
但是,ActionScript 3.0 引入了无类型变量这一概念,这一类变量可通过以下两种方法来指定:
var someObj:*;
var someObj;
无类型变量与 Object 类型的变量不同。二者的主要区别在于无类型变量可以保存特殊值 undefined,而 Object 类型的变量则不能保存该值。
您可以使用 class 关键字来定义自己的类。在方法声明中,可通过以下三种方法来声明类属性 (property):用 const 关键字定义常量,用 var 关键字定义变量,用 get 和 set 属性 (attribute) 定义 getter 和 setter 属性 (property)。可以用 function 关键字来声明方法。
可使用 new 运算符来创建类的实例。
包和命名空间
包和命名空间是两个相关的概念。使用包,可以通过有利于共享代码并尽可能减少命名冲突的方式将多个类定义捆绑在一起。使用命名空间,可以控制标识符(如属性名和方法名)的可见性。无论命名空间位于包的内部还是外部,都可以应用于代码。包可用于组织类文件,命名空间可用于管理各个属性和方法的可见性。


在 ActionScript 3.0 中,包是用命名空间实现的,但包和命名空间并不同义。在声明包时,可以隐式创建一个特殊类型的命名空间并保证它在编译时是已知的。显式创建的命名空间在编译时不必是已知的。
类位于包中,因此编译器在编译时会自动将其类名称限定为完全限定名称,编译器还限定任何属性或方法的名称。完全限定的包引用点运算符 (.) 来表示。
许多开发人员(尤其是那些具有 Java 编程背景的人)可能会选择只将类放在包的顶级。但是,ActionScript 3.0 不但支持将类放在包的顶级,而且还支持将变量、函数甚至语句放在包的顶级。此功能的一个高级用法是,在包的顶级定义一个命名空间,以便它对于该包中的所有类均可用。但是,请注意,在包的顶级只允许使用两个访问说明符:public 和 internal。Java 允许将嵌套类声明为私有,而 ActionScript 3.0 则不同,它既不支持嵌套类也不支持私有类。
您还可以在包名称中嵌入点来创建嵌套包,这样就可以创建包的分层结构。
创建包
ActionScript 3.0 允许在一个源文件中包括多个类,但是,每个文件中只有一个类可供该文件外部的代码使用。换言之,每个文件中只有一个类可以在包声明中进行声明。您必须在包定义的外部声明其它任何类,以使这些类对于该源文件外部的代码不可见。在包定义内部声明的类的名称必须与源文件的名称匹配。
在 ActionScript 3.0 中,尽管包仍表示目录,但是它现在不只包含类。在 ActionScript 3.0 中,使用 package 语句来声明包,这意味着您还可以在包的顶级声明变量、函数和命名空间,甚至还可以在包的顶级包括可执行语句。如果在包的顶级声明变量、函数或命名空间,则在顶级只能使用 public 和 internal 属性,并且每个文件中只能有一个包级声明使用 public 属性(无论该声明是类声明、变量声明、函数声明还是命名空间声明)。
包的作用是组织代码并防止名称冲突。您不应将包的概念与类继承这一不相关的概念混淆。位于同一个包中的两个类具有共同的命名空间,但是它们在其它任何方面都不必相关。同样,在语义方面,嵌套包可以与其父包无关。
导入包
如果您希望使用位于某个包内部的特定类,则必须导入该包或该类。
通常,import 语句越具体越好。如果您只打算使用包中的某个类,则应只导入该类,而不应导入该类所属的整个包。导入整个包可能会导致意外的名称冲突。
还必须将定义包或类的源代码放在类路径 内部。类路径是用户定义的本地目录路径列表,它决定了编译器将在何处搜索导入的包和类。类路径有时称为"生成路径"或"源路径"。
在正确地导入类或包之后,可以使用类的完全限定名称,也可以只使用类名称本身。
当同名的类、方法或属性会导致代码不明确时,完全限定的名称非常有用,但是,如果将它用于所有的标识符,则会使代码变得难以管理。包的嵌套级别越高,代码的可读性越差。如果您确信不明确的标识符不会导致问题,就可以通过使用简单的标识符来提高代码的可读性。
如果您尝试使用标识符名称,而不先导入相应的包或类,编译器将找不到类定义。另一方面,即便您导入了包或类,只要尝试定义的名称与所导入的名称冲突,也会产生错误。
创建包时,该包的所有成员的默认访问说明符是 internal,这意味着,默认情况下,包成员仅对其所在包的其它成员可见。如果您希望某个类对包外部的代码可用,则必须将该类声明为 public。
不能将 public 属性应用于包声明。
完全限定的名称可用来解决在使用包时可能发生的名称冲突。如果您导入两个包,但它们用同一个标识符来定义类,就可能会发生名称冲突。要解决此冲突,必须使用每个类的完全限定名称。
ActionScript 3.0有一个include 指令,但是它的作用不是为了导入类和包。要在ActionScript 3.0中导入类或包,必须使用 import 语句,并将包含该包的源文件放在类路径中。
命名空间
通过命名空间可以控制所创建的属性和方法的可见性。请将 public、private、protected 和 internal 访问控制说明符视为内置的命名空间。如果这些预定义的访问控制说明符无法满足您的要求,您可以创建自己的命名空间。
要了解命名空间的工作方式,有必要先了解属性或方法的名称总是包含两部分:标识符和命名空间。标识符通常被视为名称。
只要定义不以命名空间属性开头,就会用默认 internal 命名空间限定其名称,这意味着,它们仅对同一个包中的调用方可见。如果编译器设置为严格模式,则编译器会发出一个警告,指明 internal 命名空间将应用于没有命名空间属性的任何标识符。为了确保标识符可在任何位置使用,您必须在标识符名称的前面明确加上 public 属性。
使用命名空间时,应遵循以下三个基本步骤。
第一,必须使用 namespace 关键字来定义命名空间。例如,下面的代码定义 version1 命名空间:
namespace version1;
第二,在属性或方法声明中,使用命名空间(而非访问控制说明符)来应用命名空间。下面的示例将一个名为 myFunction() 的函数放在 version1 命名空间中:
version1 function myFunction() {}
第三,在应用了该命名空间后,可以使用 use 指令引用它,也可以使用该命名空间来限定标识符的名称。下面的示例通过 use 指令来引用 myFunction() 函数:
use namespace version1;
myFunction();
您还可以使用限定名称来引用 myFunction() 函数,如下面的示例所示:
version1::myFunction();
定义命名空间
命名空间中包含一个名为统一资源标识符 (URI) 的值,该值有时称为命名空间名称。使用 URI 可确保命名空间定义的唯一性。(统一资源标识符 (Uniform Resource Identifier, URI) 用于唯一地标识元素或属性的数字或名称。URI 包括统一资源名称 (URN) 和统一资源定位器 (URL)。)
可通过使用以下两种方法之一来声明命名空间定义,以创建命名空间:像定义 XML 命名空间那样使用显式 URI 定义命名空间;省略 URI。
下面的示例说明如何使用 URI 来定义命名空间:
namespace flash_proxy = "http://www.loach.net.cnflash/proxy";
URI 用作该命名空间的唯一标识字符串。如果您省略 URI(如下面的示例所示),则编译器将创建一个唯一的内部标识字符串来代替 URI。您对于这个内部标识字符串不具有访问权限。
namespace flash_proxy;
在定义了命名空间(具有 URI 或没有 URI)后,就不能在同一个作用域内重新定义该命名空间。如果尝试定义的命名空间以前在同一个作用域内定义过,则将生成编译器错误。
如果在某个包或类中定义了一个命名空间,则该命名空间可能对于此包或类外部的代码不可见,除非使用了相应的访问控制说明符。例如,下面的代码显示了在 flash.utils 包中定义的 flash_proxy 命名空间。在下面的示例中,缺乏访问控制说明符意味着 flash_proxy 命名空间将仅对于 flash.utils 包内部的代码可见,而对于该包外部的任何代码都不可见:
package flash.utils
{
namespace flash_proxy;
}
下面的代码使用 public 属性以使 flash_proxy 命名空间对该包外部的代码可见:
package flash.utils
{
public namespace flash_proxy;
}
应用命名空间
应用命名空间意味着在命名空间中放置定义。可以放在命名空间中的定义包括函数、变量和常量(不能将类放在自定义命名空间中)。
例如,请考虑一个使用 public 访问控制命名空间声明的函数。在函数的定义中使用 public 属性会将该函数放在 public 命名空间中,从而使该函数对于所有的代码都可用。在定义了某个命名空间之后,可以按照与使用 public 属性相同的方式来使用所定义的命名空间,该定义将对于可以引用您的自定义命名空间的代码可用。例如,如果您定义一个名为 example1 的命名空间,则可以添加一个名为 myFunction() 的方法并将 example1 用作属性,如下面的示例所示:
namespace example1;
class someClass
{
example1 myFunction() {}
}
如果在声明 myFunction() 方法时将 example1 命名空间用作属性,则意味着该方法属于 example1 命名空间。
在应用命名空间时,应切记以下几点:
对于每个声明只能应用一个命名空间。
不能一次将同一个命名空间属性应用于多个定义。换言之,如果您希望将自己的命名空间应用于 10 个不同的函数,则必须将该命名空间作为属性分别添加到这 10 个函数的定义中。
如果您应用了命名空间,则不能同时指定访问控制说明符,因为命名空间和访问控制说明符是互斥的。换言之,如果应用了命名空间,就不能将函数或属性声明为 public、private、protected 或 internal。
引用命名空间
在使用借助于任何访问控制命名空间(如 public、private、protected 和 internal)声明的方法或属性时,无需显式引用命名空间。这是因为对于这些特殊命名空间的访问由上下文控制。例如,放在 private 命名空间中的定义会自动对于同一个类中的代码可用。但是,对于您所定义的命名空间,并不存在这样的上下文相关性。要使用已经放在某个自定义命名空间中的方法或属性,必须引用该命名空间。
可以用 use namespace 指令来引用命名空间,也可以使用名称限定符 (::) 来引用命名空间限定名称。用 use namespace 指令引用命名空间会打开该命名空间,这样它便可以应用于任何未限定的标识符。例如,如果您已经定义了 example1 命名空间,则可以通过使用 use namespace example1 来访问该命名空间中的名称:
use namespace example1;
myFunction();
一次可以打开多个命名空间。在使用 use namespace 打开了某个命名空间之后,它会在打开它的整个代码块中保持打开状态。不能显式关闭命名空间。
但是,如果同时打开多个命名空间则会增加发生名称冲突的可能性。如果您不愿意打开命名空间,则可以用命名空间和名称限定符来限定方法或属性名,从而避免使用 use namespace 指令。例如,下面的代码说明如何用 example1 命名空间来限定 myFunction() 名称:
example1::myFunction();
使用命名空间
在 Flash Player API 中的 flash.utils.Proxy 类中,可以找到用来防止名称冲突的命名空间的实例。Proxy 类取代了 ActionScript 2.0 中的 Object.__resolve 属性,可用来截获对未定义的属性或方法的引用,以免发生错误。为了避免名称冲突,将 Proxy 类的所有方法都放在 flash_proxy 命名空间中。
为了更好地了解 flash_proxy 命名空间的使用方法,您需要了解如何使用 Proxy 类。Proxy 类的功能仅对于继承它的类可用。换言之,如果您要对某个对象使用 Proxy 类的方法,则该对象的类定义必须是对 Proxy 类的扩展。例如,如果您希望截获对未定义的方法的调用,则应扩展 Proxy 类,然后覆盖 Proxy 类的 callProperty() 方法。
前面已讲到,实现命名空间的过程通常分为三步,即定义、应用然后引用命名空间。但是,由于您从不显式调用 Proxy 类的任何方法,因此只是定义和应用 flash_proxy 命名空间,而从不引用它。Flash Player API 定义 flash_proxy 命名空间并在 Proxy 类中应用它。在您的代码中,只需要将 flash_proxy 命名空间应用于扩展 Proxy 类的类。
flash_proxy 命名空间按照与下面类似的方法在 flash.utils 包中定义:
package flash.utils
{
public namespace flash_proxy;
}
该命名空间将应用于 Proxy 类的方法,如下面摘自 Proxy 类的代码所示:
public class Proxy
{
flash_proxy function callProperty(name:*, ... rest):*
flash_proxy function deleteProperty(name:*):Boolean
...
}
如下面的代码所示,您必须先导入 Proxy 类和 flash_proxy 命名空间。随后必须声明自己的类,以便它对 Proxy 类进行扩展(如果是在严格模式下进行编译,则还必须添加 dynamic 属性)。在覆盖 callProperty() 方法时,必须使用 flash_proxy 命名空间。
package
{
import flash.utils.Proxy;
import flash.utils.flash_proxy;

dynamic class MyProxy extends Proxy
{
flash_proxy override function callProperty(name:*, ...rest):*
{
trace("method call intercepted: " + name);
}
}
}
如果您创建 MyProxy 类的一个实例,并调用一个未定义的方法(如在下面的示例中调用的 testing() 方法),Proxy 对象将截获对该方法的调用,并执行覆盖后的 callProperty() 方法内部的语句(在本例中为一个简单的 trace() 语句)。
var mySample:MyProxy = new MyProxy();
mySample.testing(); // 已截获方法调用:测试
将 Proxy 类的方法放在 flash_proxy 命名空间内部有两个好处。第一个好处是,在扩展 Proxy 类的任何类的公共接口中,拥有单独的命名空间可提高代码的可读性。(在 Proxy 类中大约有 12 个可以覆盖的方法,所有这些方法都不能直接调用。将所有这些方法都放在公共命名空间中可能会引起混淆。)第二个好处是,当 Proxy 子类中包含名称与 Proxy 类方法的名称匹配的实例方法时,使用 flash_proxy 命名空间可避免名称冲突。例如,您可能希望将自己的某个方法命名为 callProperty()。下面的代码是可接受的,因为您所用的 callProperty() 方法位于另一个命名空间中:
dynamic class MyProxy extends Proxy
{
public function callProperty() {}
flash_proxy override function callProperty(name:*, ...rest):*
{
trace("method call intercepted: " + name);
}
}
当您希望以一种无法由四个访问控制说明符(public、private、internal 和 protected)实现的方式提供对方法或属性的访问时,命名空间也可能会非常有用。例如,您可能有几个分散在多个包中的实用程序方法。您希望这些方法对于您的所有包均可用,但是您不希望这些方法成为公共方法。为此,您可以创建一个新的命名空间,并将它用作您自己的特殊访问控制说明符。
下面的示例使用用户定义的命名空间将两个位于不同包中的函数组合在一起。通过将它们组合到同一个命名空间中,可以通过一条 use namespace 语句使这两个函数对于某个类或某个包均可见。
本示例使用四个文件来说明此方法。所有的文件都必须位于您的类路径中。第一个文件 (myInternal.as) 用来定义 myInternal 命名空间。由于该文件位于名为 example 的包中,因此您必须将该文件放在名为 example 的文件夹中。该命名空间标记为 public,因此可以导入到其它包中。
// example 文件夹中的 myInternal.as
package example
{
public namespace myInternal = "http://www.loach.net.cn2006/actionscript/examples";
}
第二个文件 (Utility.as) 和第三个文件 (Helper.as) 定义的类中包含应可供其它包使用的方法。Utility 类位于 example.alpha 包中,这意味着该文件应放在 example 文件夹下的 alpha 子文件夹中。Helper 类位于 example.beta 包中,这意味着该文件应放在 example 文件夹下的 beta 子文件夹中。这两个包(example.alpha 和 example.beta)在使用命名空间之前必须先导入它。
// example/alpha 文件夹中的 Utility.as
package example.alpha
{
import example.myInternal;

public class Utility
{
private static var _taskCounter:int = 0;

public static function someTask()
{
_taskCounter++;
}

myInternal static function get taskCounter():int
{
return _taskCounter;
}
}
}

// example/beta 文件夹中的 Helper.as
package example.beta
{
import example.myInternal;

public class Helper
{
private static var _timeStamp:Date;

public static function someTask()
{
_timeStamp = new Date();
}

myInternal static function get lastCalled():Date
{
return _timeStamp;
}
}
}
第四个文件 (NamespaceUseCase.as) 是主应用程序类,应是 example 文件夹的同级。在 Adobe Flash CS3 Professional 中,将此类用作 FLA 的文档类。NamespaceUseCase 类还导入 myInternal 命名空间,并使用它来调用位于其它包中的两个静态方法。在本示例中,使用静态方法的目的仅在于简化代码。在 myInternal 命名空间中既可以放置静态方法也可以放置实例方法。
// NamespaceUseCase.as
package
{
import flash.display.MovieClip;
import example.myInternal; // 导入命名空间
import example.alpha.Utility; // 导入 Utility 类
import example.beta.Helper; // 导入 Helper 类

public class NamespaceUseCase extends MovieClip
{
public function NamespaceUseCase()
{
use namespace myInternal;

Utility.someTask();
Utility.someTask();
trace(Utility.taskCounter); // 2

Helper.someTask();
trace(Helper.lastCalled); // [上次调用 someTask() 的时间]
}
}
}
变量
变量可用来存储程序中使用的值。要声明变量,必须将 var 语句和变量名结合使用。在 ActionScript 3.0 中,总是需要使用 var 语句。
如果在声明变量时省略了 var 语句,在严格模式下将出现编译器错误,在标准模式下将出现运行时错误。
要将变量与一个数据类型相关联,则必须在声明变量时进行此操作。在声明变量时不指定变量的类型是合法的,但这在严格模式下将产生编译器警告。可通过在变量名后面追加一个后跟变量类型的冒号 (:) 来指定变量类型。
可以使用赋值运算符 (=) 为变量赋值。您可能会发现在声明变量的同时为变量赋值可能更加方便。通常,在声明变量的同时为变量赋值的方法不仅在赋予基元值(如整数和字符串)时很常用,而且在创建数组或实例化类的实例时也很常用。
如果要声明多个变量,则可以使用逗号运算符 (,) 来分隔变量,从而在一行代码中声明所有这些变量。也可以在同一行代码中为其中的每个变量赋值。
了解变量的作用域
变量的"作用域"是指可在其中通过引用词汇来访问变量的代码区域。"全局"变量是指在代码的所有区域中定义的变量,而"局部"变量是指仅在代码的某个部分定义的变量。在 ActionScript 3.0 中,始终为变量分配声明它们的函数或类的作用域。全局变量是在任何函数或类定义的外部定义的变量。全局变量在函数定义的内部和外部均可用。
可以通过在函数定义内部声明变量来将它声明为局部变量。可定义局部变量的最小代码区域就是函数定义。在函数内部声明的局部变量仅存在于该函数中。该变量在该函数外部将不可用。
如果用于局部变量的变量名已经被声明为全局变量,那么,当局部变量在作用域内时,局部定义会隐藏(或遮蔽)全局定义。全局变量在该函数外部仍然存在。
代码块是指左大括号 ({) 与右大括号 (}) 之间的任意一组语句。在在ActionScript 中,如果您在某个代码块中声明一个变量,那么,该变量不仅在该代码块中可用,而且还在该代码块所属函数的其它任何部分都可用。
有趣的是,如果缺乏块级作用域,那么,只要在函数结束之前对变量进行声明,就可以在声明变量之前读写它。这是由于存在一种名为"提升"的方法,该方法表示编译器会将所有的变量声明移到函数的顶部。
但是,编译器将不会提升任何赋值语句。
这意味着您甚至可以在声明变量之前为变量赋值。
默认值
"默认值"是在设置变量值之前变量中包含的值。首次设置变量的值实际上就是"初始化"变量。如果您声明了一个变量,但是没有设置它的值,则该变量便处于"未初始化"状态。未初始化的变量的值取决于它的数据类型。
下表说明了变量的默认值,并按数据类型对这些值进行组织:
数据类型
默认值
Boolean
false
int
0
Number
NaN
Object
null
String
null
uint
0
未声明(与类型注释 * 等效)
undefined
其它所有类(包括用户定义的类)。
null
对于 Number 类型的变量,默认值是 NaN(而非某个数字),NaN 是一个由 IEEE-754 标准定义的特殊值,它表示非数字的某个值。
如果您声明某个变量,但是未声明它的数据类型,则将应用默认数据类型 *,这实际上表示该变量是无类型变量。如果您没有用值初始化无类型变量,则该变量的默认值是 undefined。
对于 Boolean、Number、int 和 uint 以外的数据类型,所有未初始化变量的默认值都是 null。这适用于由 Flash Player API 定义的所有类以及您创建的所有自定义类。
对于 Boolean、Number、int 或 uint 类型的变量,null 不是有效值。如果您尝试将值 null 赋予这样的变量,则该值会转换为该数据类型的默认值。对于 Object 类型的变量,可以赋予 null 值。如果您尝试将值 undefined 赋予 Object 类型的变量,则该值会转换为 null。
对于 Number 类型的变量,有一个名为 isNaN() 的特殊的顶级函数。如果变量不是数字,该函数将返回布尔值 true,否则将返回 false。
数据类型
"数据类型"用来定义一组值。ActionScript 3.0 中的所有值均是对象,而与它们是基元值还是复杂值无关。
"基元值"是一个属于下列数据类型之一的值:Boolean、int、Number、String 和 uint。基元值的处理速度通常比复杂值的处理速度快,因为 ActionScript 按照一种尽可能优化内存和提高速度的特殊方式来存储基元值。
ActionScript 在内部将基元值作为不可改变的对象进行存储。这意味着按引用传递与按值传递同样有效。这可以减少内存的使用量并提高执行速度,因为引用通常比值本身小得多。
"复杂值"是指基元值以外的值。定义复杂值的集合的数据类型包括:Array、Date、Error、Function、RegExp、XML 和 XMLList。
在 ActionScript 3.0 中,出于实用的目的,不对基元值及其包装对象加以区分。所有的值(甚至基元值)都是对象。Flash Player 将这些基元类型视为特例 ─ 它们的行为与对象相似,但是不需要创建对象所涉及的正常开销。
上面列出的所有基元数据类型和复杂数据类型都是由 ActionScript 3.0 核心类定义的。通过 ActionScript 3.0 核心类,可以使用字面值(而非 new 运算符)创建对象。
类型检查
ActionScript 3.0 是动态类型的语言,它在运行时执行类型检查,同时也支持在名为"严格模式"的特殊编译器模式下在编译时执行类型检查。在严格模式下,类型检查既发生在编译时也发生在运行时,但是在标准模式下,类型检查仅发生在运行时。
在构造代码时,动态类型的语言带来了极大的灵活性,但代价是在运行时可能出现类型错误。静态类型的语言在编译时报告类型错误,但代价是要求类型信息在编译时是已知的。
编译时类型检查
在较大的项目中通常建议使用编译时类型检查,因为随着项目变大,相对于尽早捕获类型错误,数据类型的灵活性通常会变得不那么重要。这就是为什么将 Adobe Flash CS3 Professional 和 Adobe Flex Builder 2 中的 ActionScript 编译器默认设置为在严格模式下运行的原因。
为了提供编译时类型检查,编译器需要知道代码中的变量或表达式的数据类型信息。为了显式声明变量的数据类型,请在变量名后面添加后跟数据类型的冒号运算符 (:) 作为其后缀。要将数据类型与参数相关联,应使用后跟数据类型的冒号运算符。
在严格模式下,ActionScript 编译器将类型不匹配报告为编译器错误。
但是,即使在严格模式下,也可以选择不在赋值语句右侧指定类型,从而退出编译时类型检查。可以通过省略类型注释或使用特殊的星号 (*) 类型注释,来将变量或表达式标记为无类型。
运行时类型检查
在 ActionScript 3.0 中,无论是在严格模式下还是在标准模式下编译,在运行时都将进行类型检查。
还可能会出现如下情形:即使在严格模式下运行,也可能会获得运行时类型错误。如果您使用严格模式,但是通过使用无类型变量而退出了编译时类型检查,就可能会出现上述情形。当您使用无类型变量时,并不会消除类型检查,而只是将其延迟到运行时执行。
与编译时类型检查相比,运行时类型检查还允许更灵活地使用继承。标准模式会将类型检查延迟到运行时执行,从而允许您引用子类的属性,即使您"上传"也是如此。当您使用基类来声明类实例的类型,但是使用子类构造函数来实例化类实例时,就会发生上传。上传被视为安全操作,这是因为基类不包含子类中没有的任何属性或方法。但是,子类中则包含其基类中没有的属性或方法。
is 运算符
is 运算符是 ActionScript 3.0 中的新增运算符,它可用来测试变量或表达式是否为给定数据类型的成员。is 运算符检查正确的继承层次结构,不但可以用来检查对象是否为特定类的实例,而且还可以检查对象是
否是用来实现特定接口的类的实例。
as 运算符
as 运算符是 ActionScript 3.0 中的新增运算符,也可用来检查表达式是否为给定数据类型的成员。但是,与 is 运算符不同的是,as 运算符不返回布尔值,而是返回表达式的值(代替 true)或 null(代替 false)。
在使用 as 运算符时,右侧的操作数必须是数据类型。如果尝试使用表达式(而非数据类型)作为右侧的操作数,将会产生错误。
动态类
"动态"类定义在运行时可通过添加/更改属性和方法来改变的对象。非动态类(如 String 类)是"密封"类。您不能在运行时向密封类中添加属性或方法。
在声明类时,可以通过使用 dynamic 属性来创建动态类。
如果要在以后实例化动态类的实例,则可以在类定义的外部向该类中添加属性或方法。添加到动态类实例中的属性是运行时实体,因此会在运行时完成所有类型检查。不能向以这种方式添加的属性中添加类型注释。您还可以定义一个函数并将该函数附加到动态类实例的某个属性,从而向动态类实例中添加方法。但是,以这种方式创建的方法对于动态类的任何私有属性或方法都不具有访问权限。而且,即使对动态类的公共属性或方法的引用也必须用 this 关键字或类名进行限定。
数据类型说明
基元数据类型包括 Boolean、int、Null、Number、String、uint 和 void。ActionScript 核心类还定义下列复杂数据类型:Object、Array、Date、Error、Function、RegExp、XML 和 XMLList。
Boolean 数据类型
Boolean 数据类型包含两个值:true 和 false。对于 Boolean 类型的变量,其它任何值都是无效的。已经声明但尚未初始化的布尔变量的默认值是 false。
int 数据类型
int 数据类型在内部存储为 32 位整数,它包含一组介于 -2,147,483,648 (-231) 和 2,147,483,647 (231 - 1) 之间的整数(包括 -2,147,483,648 和 2,147,483,647)。早期的 ActionScript 版本仅提供 Number 数据类型,该数据类型既可用于整数又可用于浮点数。在 ActionScript 3.0 中,现在可以访问 32 位带符号整数和无符号整数的低位机器类型。如果您的变量将不会使用浮点数,那么,使用 int 数据类型来代替 Number 数据类型应会更快更高效。
对于小于 int 的最小值或大于 int 的最大值的整数值,应使用 Number 数据类型。Number 数据类型可以处理 -9,007,199,254,740,992 和 9,007,199,254,740,992(53 位整数值)之间的值。int 数据类型的变量的默认值是 0。
Null 数据类型
Null 数据类型仅包含一个值:null。这是 String 数据类型和用来定义复杂数据类型的所有类(包括 Object 类)的默认值。其它基元数据类型(如 Boolean、Number、int 和 uint)均不包含 null 值。如果您尝试向 Boolean、Number、int 或 uint 类型的变量赋予 null,则 Flash Player 会将 null 值转换为相应的默认值。不能将 Null 数据类型用作类型注释。
Number 数据类型
在 ActionScript 3.0 中,Number 数据类型可以表示整数、无符号整数和浮点数。但是,为了尽可能提高性能,应将 Number 数据类型仅用于浮点数,或者用于 int 和 uint 类型可以存储的、大于 32 位的整数值。要存储浮点数,数字中应包括一个小数点。如果您省略了小数点,数字将存储为整数。
Number 数据类型使用由 IEEE 二进制浮点算术标准 (IEEE-754) 指定的 64 位双精度格式。此标准规定如何使用 64 个可用位来存储浮点数。其中的 1 位用来指定数字是正数还是负数。11 位用于指数,它以二进制的形式存储。其余的 52 位用于存储"有效位数"(又称为"尾数"),有效位数是 2 的 N 次幂,N 即前面所提到的指数。
可以将 Number 数据类型的所有位都用于有效位数,也可以将 Number 数据类型的某些位用于存储指数,后者可存储的浮点数比前者大得多。
Number 类型可以表示的最小值和最大值存储在 Number 类的名为 Number.MAX_VALUE 和 Number.MIN_VALUE 的静态属性中。
Number.MAX_VALUE == 1.79769313486231e+308
Number.MIN_VALUE == 4.940656458412467e-324
尽管这个数字范围很大,但代价是此范围的精度有所降低。Number 数据类型使用 52 位来存储有效位数,因此,那些要求用 52 位以上的位数才能精确表示的数字(如分数 1/3)将只是近似值。如果应用程序要求小数达到绝对精度,则需要使用实现小数浮点算术(而非二进制浮点算术)的软件。
如果用 Number 数据类型来存储整数值,则仅使用 52 位有效位数。Number 数据类型使用 52 位和一个特殊的隐藏位来表示介于 -9,007,199,254,740,992 (-253) 和 9,007,199,254,740,992 (253) 之间的整数。
Flash Player 不但将 NaN 值用作 Number 类型的变量的默认值,而且还将其用作应返回数字、却没有返回数字的任何运算的结果。其它特殊的 Number 值包括"正无穷大"和"负无穷大"。
在被 0 除时,如果被除数也是 0,则结果只有一个,那就是 NaN。在被 0 除时,如果被除数是正数,则结果为正无穷大;如果被除数是负数,则结果为负无穷大。
String 数据类型
String 数据类型表示一个 16 位字符的序列。字符串在内部存储为 Unicode 字符,并使用 UTF-16 格式。字符串是不可改变的值,就像在 Java 编程语言中一样。对字符串值执行运算会返回字符串的一个新实例。用 String 数据类型声明的变量的默认值是 null。虽然 null 值与空字符串 ("") 均表示没有任何字符,但二者并不相同。
uint 数据类型
uint 数据类型在内部存储为 32 位无符号整数,它包含一组介于 0 和 4,294,967,295 (232- 1) 之间的整数(包括 0 和 4,294,967,295)。uint 数据类型可用于要求非负整数的特殊情形。例如,必须使用 uint 数据类型来表示像素颜色值,因为 int 数据类型有一个内部符号位,该符号位并不适合处理颜色值。对于大于 uint 的最大值的整数值,应使用 Number 数据类型,该数据类型可以处理 53 位整数值。uint 数据类型的变量的默认值是 0。
void 数据类型
void 数据类型仅包含一个值:undefined。在 ActionScript 3.0 中,Object 实例的默认值是 null。如果您尝试将值 undefined 赋予 Object 类的实例,Flash Player 会将该值转换为 null。您只能为无类型变量赋予 undefined 这一值。无类型变量是指缺乏类型注释或者使用星号 (*) 作为类型注释的变量。只能将 void 用作返回类型注释。
Object 数据类型
Object 数据类型是由 Object 类定义的。Object 类用作 ActionScript 中的所有类定义的基类。ActionScript 3.0 中的 Object 数据类型与早期版本中的 Object 数据类型存在以下三方面的区别:第一,Object 数据类型不再是指定给没有类型注释的变量的默认数据类型。第二,Object 数据类型不再包括 undefined 这一值,该值以前是 Object 实例的默认值。第三,在 ActionScript 3.0 中,Object 类实例的默认值是 null。
在早期的 ActionScript 版本中,会自动为没有类型注释的变量赋予 Object 数据类型。ActionScript 3.0 现在包括真正无类型变量这一概念,因此不再为没有类型注释的变量赋予 Object 数据类型。没有类型注释的变量现在被视为无类型变量。如果您希望向代码的读者清楚地表明您是故意将变量保留为无类型,可以使用新的星号 (*) 表示类型注释,这与省略类型注释等效。
只有无类型变量才能保存值 undefined。如果您尝试将值 undefined 赋给具有数据类型的变量,Flash Player 会将该值 undefined 转换为该数据类型的默认值。对于 Object 数据类型的实例,默认值是 null,这意味着,如果尝试将 undefined 赋给 Object 实例,Flash Player 会将值 undefined 转换为 null。
类型转换
在将某个值转换为其它数据类型的值时,就说发生了类型转换。类型转换可以是"隐式的",也可以是"显式的"。隐式转换又称为"强制",有时由 Flash Player 在运行时执行。显式转换又称为"转换",在代码指示编译器将一个数据类型的变量视为属于另一个数据类型时发生。在涉及基元值时,转换功能将一个数据类型的值实际转换为另一个数据类型的值。
要将对象转换为另一类型,请用小括号括起对象名并在它前面加上新类型的名称。
隐式转换
对于用户定义的类型,当要转换的值是目标类(或者派生自目标类的类)的实例时,隐式转换会成功。如果隐式转换不成功,就会出现错误。
对于基元类型而言,隐式转换是通过调用内部转换算法来处理的,该算法与显式转换函数所调用的算法相同。
显式转换
在严格模式下进行编译时,使用显式转换会非常有用,因为您有时可能会不希望因类型不匹配而生成编译时错误。当您知道强制功能会在运行时正确转换您的值时,可能就属于这种情况。
转换为 int、uint 和 Number
您可以将任何数据类型转换为以下三种数字类型之一:int、uint 和 Number。如果 Flash Player 由于某种原因而无法转换数字,则会为 int 和 uint 数据类型赋予默认值 0,为 Number 数据类型赋予默认值 NaN。如果将布尔值转换为数字,则 true 变成值 1,false 变成值 0。
仅包含数字的字符串值可以成功地转换为数字类型之一。看上去像负数的字符串或者表示十六进制值的字符串(例如,0x1A)也可以转换为数字类型。转换过程中会忽略字符串值中的前导或尾随空白字符。还可以使用 Number() 来转换看上去像浮点数的字符串。如果包含小数点,则会导致 uint() 和 int() 返回一个整数,小数点和它后面的字符被截断。
对于包含非数字字符的字符串值,在用 int() 或 uint() 转换时,将返回 0;在用 Number() 转换时,将返回 NaN。转换过程中会忽略前导和尾随空白,但是,如果字符串中包含将两个数字隔开的空白,则将返回 0 或 NaN。
在 ActionScript 3.0 中,Number() 函数不再支持八进制数或基数为 8 的数。对于 ActionScript 3.0 中的 Number() 函数, 会忽略前导 0。
将一种数值类型的值赋给另一种数值类型的变量时,转换并不是必需的。即使在严格模式下,数值类型也会隐式转换为其它数值类型。这意味着,在某些情况下,在超出类型的范围时,可能会生成意外的值。
var myUInt:uint = -3; // 将 int/Number 值赋给 uint 变量
trace(myUInt); // 4294967293
var myNum:Number = sampleUINT; // 将 int/uint 值赋给 Number 变量
trace(myNum) // 4294967293
var myInt:int = uint.MAX_VALUE + 1; // 将 Number 值赋给 uint 变量
trace(myInt); // 0
myInt = int.MAX_VALUE + 1; // 将 uint/Number 值赋给 int 变量
trace(myInt); // -2147483648
下表概述了将其它数据类型转换为 Number、int 或 uint 数据类型的结果。
数据类型或值
转换为 Number、int 或 uint 时的结果
Boolean
如果值为 true,则结果为 1;否则为 0。
Date
Date 对象的内部表示形式,即从 1970 年 1 月 1 日午夜(通用时间)以来所经过的毫秒数。
null
0
Object
如果实例为 null 并转换为 Number,则结果为 NaN;否则为 0。
String
如果 Flash Player 可以将字符串转换为数字,则结果为数字;否则,如果转换为 Number,则结果为 NaN,如果转换为 int 或 uint,则结果为 0。
undefined
如果转换为 Number,则结果为 NaN;如果转换为 int 或 uint,则结果为 0。
转换为 Boolean
在从任何数值数据类型(uint、int 和 Number)转换为 Boolean 时,如果数值为 0,则结果为 false;否则为 true。对于 Number 数据类型,如果值为 NaN,则结果也为 false。
在将字符串值转换为 Boolean 数据类型时,如果字符串为 null 或空字符串 (""),则会返回 false。否则,将返回 true。
在将 Object 类的实例转换为 Boolean 数据类型时,如果该实例为 null,则将返回 false;否则将返回 true。
在严格模式下,系统会对布尔变量进行特殊处理,因为您不必转换即可向布尔变量赋予任何数据类型的值。即使在严格模式下,也可以将所有的数据类型隐式强制为 Boolean 数据类型。换言之,与几乎其它所有数据类型不同,转换为 Boolean 数据类型不是避免在严格模式下出错所必需的。
下表概述了在从其它数据类型转换为 Boolean 数据类型时的结果:
数据类型或值
转换为 Boolean 数据类型时的结果
String
如果值为 null 或空字符串 (""),则结果为 false;否则为 true。
null
false
Number、int 或 uint
如果值为 NaN 或 0,则结果为 false;否则为 true。
Object
如果实例为 null,则结果为 false;否则为 true。
转换为 String
从任何数值数据类型转换为 String 数据类型时,都会返回数字的字符串表示形式。在将布尔值转换为 String 数据类型时,如果值为 true,则返回字符串 "true";如果值为 false,则返回字符串 "false"。
在从 Object 类的实例转换为 String 数据类型时,如果该实例为 null,则返回字符串 "null"。否则,将返回字符串 "[object Object]"。
在从 Array 类的实例转换为 String 时,会返回一个字符串,其中包含所有数组元素的逗号分隔列表。
在从 Date 类的实例转换为 String 时,会返回该实例所包含日期的字符串表示形式。(输出结果显示的是太平洋夏令时)


下表概述了在将其它数据类型转换为 String 数据类型时的结果:
数据类型或值
转换为 String 数据类型时的结果
Array
一个包含所有数组元素的字符串。
Boolean
"true" 或 "false"。
Date
Date 对象的字符串表示形式。
null
"null"
Number、int 或 uint
数字的字符串表示形式。
Object
如果实例为 null,则结果为 "null";否则为 "[object Object]"。
语法
语言的语法定义了一组在编写可执行代码时必须遵循的规则。
区分大小写
ActionScript 3.0 是一种区分大小写的语言。只是大小写不同的标识符会被视为不同。
点语法
可以通过点运算符 ( . ) 来访问对象的属性和方法。使用点语法,可以使用后跟点运算符和属性名或方法名的实例名来引用类的属性或方法。
定义包时,可以使用点语法。可以使用点运算符来引用嵌套包。
字面值
"字面值"是直接出现在代码中的值。
字面值还可以组合起来构成复合字面值。数组文本括在中括号字符 ([ ]) 中,各数组元素之间用逗号隔开。数组文本可用于初始化数组。您可以使用 new 语句将复合字面值作为参数传递给 Array 类构造函数,但是,您还可以在实例化下面的 ActionScript 核心类的实例时直接赋予字面值:Object、Array、String、Number、int、uint、XML、XMLList 和 Boolean。
字面值还可用来初始化通用对象。通用对象是 Object 类的一个实例。对象字面值括在大括号 ({ }) 中,各对象属性之间用逗号隔开。每个属性都用冒号字符 ( : ) 进行声明,冒号用于分隔属性名和属性值。
可以使用 new 语句创建一个通用对象并将该对象的字面值作为参数传递给 Object 类构造函数,也可以在声明实例时直接将对象字面值赋给实例。
分号
可以使用分号字符 ( ; ) 来终止语句。如果您省略分号字符,则编译器将假设每一行代码代表一条语句。由于很多程序员都习惯使用分号来表示语句结束,因此,如果您坚持使用分号来终止语句,则代码会更易于阅读。
使用分号终止语句可以在一行中放置多个语句,但是这样会使代码变得难以阅读。
小括号
在 ActionScript 3.0 中,可以通过三种方式来使用小括号 (())。
首先,可以使用小括号来更改表达式中的运算顺序。组合到小括号中的运算总是最先执行。
第二,可以结合使用小括号和逗号运算符 (,) 来计算一系列表达式并返回最后一个表达式的结果。
第三,可以使用小括号来向函数或方法传递一个或多个参数。
注释
ActionScript 3.0 代码支持两种类型的注释:单行注释和多行注释。这些注释机制与 C++ 和 Java 中的注释机制类似。编译器将忽略标记为注释的文本。
单行注释以两个正斜杠字符 (//) 开头并持续到该行的末尾。
多行注释以一个正斜杠和一个星号 (/*) 开头,以一个星号和一个正斜杠 (*/) 结尾。
关键字和保留字
"保留字"是一些单词,因为这些单词是保留给 ActionScript 使用的,所以,不能在代码中将它们用作标识符。保留字包括"词汇关键字",编译器将词汇关键字从程序的命名空间中删除。如果您将词汇关键字用作标识符,则编译器会报告一个错误。



下表列出了 ActionScript 3.0 词汇关键字:
as
break
case
catch
class
const
continue
default
delete
do
else
extends
false
finally
for
function
if
implements
import
in
instanceof
interface
internal
is
native
new
null
package
private
protected
public
return
super
switch
this
throw
to
true
try
typeof
use
var
void
while
with



有一小组名为"句法关键字"的关键字,这些关键字可用作标识符,但是在某些上下文中具有特殊的含义。下表列出了 ActionScript 3.0 句法关键字:
each
get
set
namespace
include
dynamic
final
native
override
static


还有几个有时称为"供将来使用的保留字"的标识符。这些标识符不是为 ActionScript 3.0 保留的,但是其中的一些可能会被采用 ActionScript 3.0 的软件视为关键字。您可以在自己的代码中使用其中的许多标识符,但是 Adobe 不建议您使用它们,因为它们可能会在以后的 ActionScript 版本中作为关键字出现。
abstract
boolean
byte
cast
char
debugger
double
enum
export
float
goto
intrinsic
long
prototype
short
synchronized
throws
to
transient
type
virtual
volatile


常量
ActionScript 3.0 支持 const 语句,该语句可用来创建常量。常量是指具有无法改变的固定值的属性。只能为常量赋值一次,而且必须在最接近常量声明的位置赋值。例如,如果将常量声明为类的成员,则只能在声明过程中或者在类构造函数中为常量赋值。
如果您尝试以其它任何方法向常量赋予初始值,则会出现错误。
Flash Player API 定义了一组广泛的常量供您使用。按照惯例,ActionScript 中的常量全部使用大写字母,各个单词之间用下划线字符 ( _) 分隔。
运算符
运算符是一种特殊的函数,它们具有一个或多个操作数并返回相应的值。"操作数"是被运算符用作输入的值,通常是字面值、变量或表达式。
运算符可以是一元、二元或三元的。"一元"运算符有 1 个操作数。"二元"运算符有 2 个操作数。"三元"运算符有 3 个操作数。
有些运算符是"重载的",这意味着它们的行为因传递给它们的操作数的类型或数量而异。例如,加法运算符 (+) 就是一个重载运算符,其行为因操作数的数据类型而异。如果两个操作数都是数字,则加法运算符会返回这些值的和。如果两个操作数都是字符串,则加法运算符会返回这两个操作数连接后的结果。
运算符的行为还可能因所提供的操作数的数量而异。减法运算符 (-) 既是一元运算符又是二元运算符。对于减法运算符,如果只提供一个操作数,则该运算符会对操作数求反并返回结果;如果提供两个操作数,则减法运算符返回这两个操作数的差。

运算符的优先级和结合律
运算符的优先级和结合律决定了运算符的处理顺序。虽然对于熟悉算术的人来说,编译器先处理乘法运算符 (*) 然后再处理加法运算符 (+) 似乎是自然而然的事情,但实际上编译器要求显式指定先处理哪些运算符。此类指令统称为"运算符优先级"。ActionScript 定义了一个默认的运算符优先级,您可以使用小括号运算符 (()) 来改变它。
您可能会遇到这样的情况:同一个表达式中出现两个或更多个具有相同的优先级的运算符。在这些情况下,编译器使用"结合律"的规则来确定先处理哪个运算符。除了赋值运算符之外,所有二进制运算符都是"左结合"的,也就是说,先处理左边的运算符,然后再处理右边的运算符。赋值运算符和条件运算符 (?:) 都是"右结合"的,也就是说,先处理右边的运算符,然后再处理左边的运算符。
如果将具有相同的优先级的两个运算符用于同一个表达式中,那么,由于这两个运算符都是左结合的,因此先处理左边的运算符。您可以用括号运算符来改变默认的左结合律。您可以通过用小括号括起小于运算符及其操作数来命令编译器先处理小于运算符。
下表按优先级递减的顺序列出了 ActionScript 3.0 中的运算符。该表内同一行中的运算符具有相同的优先级。在该表中,每行运算符都比位于其下方的运算符的优先级高。

运算符
主要
[] {x:y} () f(x) new x.y x[y] <></> @ :: ..
后缀
x++ x--
一元
++x --x + - ~ ! delete typeof void
乘法
* / %
加法
+ -
按位移位
<< >> >>>
关系
< > <= >= as in instanceof is
等于
== != === !==
按位"与"
&
按位"异或"
^
按位"或"
|
逻辑"与"
&&
逻辑"或"
||
条件
?:
赋值
= *= /= %= += -= <<= >>= >>>= &= ^= |=
逗号
,
主要运算符
主要运算符包括用来创建 Array 和 Object 字面值、对表达式进行分组、调用函数、实例化类实例以及访问属性的运算符。下表列出了所有主要运算符,它们具有相同的优先级。属于E4X 规范的运算符用 (E4X) 来表示。
运算符
执行的运算
[]
初始化数组
{x:y}
初始化对象
()
对表达式进行分组
f(x)
调用函数
new
调用构造函数
x.y x[y]
访问属性
<></>
初始化 XMLList 对象 (E4X)
@
访问属性 (E4X)
::
限定名称 (E4X)
..
访问子级 XML 元素 (E4X)
后缀运算符
后缀运算符只有一个操作数,它递增或递减该操作数的值。虽然这些运算符是一元运算符,但是它们有别于其它一元运算符,被单独划归到了一个类别,因为它们具有更高的优先级和特殊的行为。在将后缀运算符用作较长表达式的一部分时,会在处理后缀运算符之前返回表达式的值。
下表列出了所有的后缀运算符,它们具有相同的优先级:
运算符
执行的运算
++
递增(后缀)
--
递减(后缀)
一元运算符
一元运算符只有一个操作数。这一组中的递增运算符 (++) 和递减运算符 (--) 是"前缀运算符",这意味着它们在表达式中出现在操作数的前面。前缀运算符与它们对应的后缀运算符不同,因为递增或递减操作是在返回整个表达式的值之前完成的。
下表列出了所有的一元运算符,它们具有相同的优先级:
运算符
执行的运算
++
递增(前缀)
--
递减(前缀)
+
一元 +
-
一元 -(非)
!
逻辑"非"
~
按位"非"
delete
删除属性
typeof
返回类型信息
void
返回 undefined 值
乘法运算符
乘法运算符具有两个操作数,它执行乘、除或求模计算。
下表列出了所有的乘法运算符,它们具有相同的优先级:
运算符
执行的运算
*
乘法
/
除法
%
求模
加法运算符
加法运算符有两个操作数,它执行加法或减法计算。下表列出了所有加法运算符,它们具有相同的优先级:
运算符
执行的运算
+
加法
-
减法
按位移位运算符
按位移位运算符有两个操作数,它将第一个操作数的各位按第二个操作数指定的长度移位。下表列出了所有按位移位运算符,它们具有相同的优先级:
运算符
执行的运算
<<
按位向左移位
>>
按位向右移位
>>>
按位无符号向右移位



关系运算符
关系运算符有两个操作数,它比较两个操作数的值,然后返回一个布尔值。下表列出了所有关系运算符,它们具有相同的优先级:
运算符
执行的运算
<
小于
>
大于
<=
小于或等于
>=
大于或等于
as
检查数据类型
in
检查对象属性
instanceof
检查原型链
is
检查数据类型
等于运算符
等于运算符有两个操作数,它比较两个操作数的值,然后返回一个布尔值。下表列出了所有等于运算符,它们具有相同的优先级:
运算符
执行的运算
==
等于
!=
不等于
===
严格等于
!==
严格不等于
按位逻辑运算符
按位逻辑运算符有两个操作数,它执行位级别的逻辑运算。按位逻辑运算符具有不同的优先级;下表按优先级递减的顺序列出了按位逻辑运算符:
运算符
执行的运算
&
按位"与"
^
按位"异或"
|
按位"或"
逻辑运算符
逻辑运算符有两个操作数,它返回布尔结果。逻辑运算符具有不同的优先级;下表按优先级递减的顺序列出了逻辑运算符:
运算符
执行的运算
&&
逻辑"与"
||
逻辑"或"
条件运算符
条件运算符是一个三元运算符,也就是说它有三个操作数。条件运算符是应用 if..else 条件语句的一种简便方法。
运算符
执行的运算
?:
条件
赋值运算符
赋值运算符有两个操作数,它根据一个操作数的值对另一个操作数进行赋值。下表列出了所有赋值运算符,它们具有相同的优先级:




运算符
执行的运算
=
赋值
*=
乘法赋值
/=
除法赋值
%=
求模赋值
+=
加法赋值
-=
减法赋值
<<=
按位向左移位赋值
>>=
按位向右移位赋值
>>>=
按位无符号向右移位赋值
&=
按位"与"赋值
^=
按位"异或"赋值
|=
按位"或"赋值

条件语句
ActionScript 3.0 提供了三个可用来控制程序流的基本条件语句。 if..else if..else if switch
if..else
if..else 条件语句用于测试一个条件,如果该条件存在,则执行一个代码块,否则执行替代代码块。
如果您不想执行替代代码块,可以仅使用 if 语句,而不用 else 语句。
if..else if
可以使用 if..else if 条件语句来测试多个条件。
switch
如果多个执行路径依赖于同一个条件表达式,则 switch 语句非常有用。它的功能大致相当于一系列 if..else if 语句,但是它更便于阅读。switch 语句不是对条件进行测试以获得布尔值,而是对表达式进行求值并使用计算结果来确定要执行的代码块。代码块以 case 语句开头,以 break 语句结尾。
循环
循环语句允许您使用一系列值或变量来反复执行一个特定的代码块。
for for..in for each..in while do..while
for
for 循环用于循环访问某个变量以获得特定范围的值。必须在 for 语句中提供 3 个表达式:一个设置了初始值的变量,一个用于确定循环何时结束的条件语句,以及一个在每次循环中都更改变量值的表达式。
for..in
for..in 循环用于循环访问对象属性或数组元素。
如果对象是自定义类的一个实例,则除非该类是动态类,否则将无法循环访问该对象的属性。即便对于动态类的实例,也只能循环访问动态添加的属性。
for each..in
for each..in 循环用于循环访问集合中的项目,它可以是 XML 或 XMLList 对象中的标签、对象属性保存的值或数组元素。您可以使用 for each..in 循环来循环访问通用对象的属性,但是与 for..in 循环不同的是,for each..in 循环中的迭代变量包含属性所保存的值,而不包含属性的名称。
如果对象是密封类的实例,则您将无法循环访问该对象的属性。即使对于动态类的实例,也无法循环访问任何固定属性(即,作为类定义的一部分定义的属性)。
while
while 循环与 if 语句相似,只要条件为 true,就会反复执行。
使用 while 循环(而非 for 循环)的一个缺点是,编写的 while 循环中更容易出现无限循环。如果省略了用来递增计数器变量的表达式,则 for 循环示例代码将无法编译,而 while 循环示例代码仍然能够编译。若没有用来递增 i 的表达式,循环将成为无限循环。
do..while
do..while 循环是一种 while 循环,它保证至少执行一次代码块,这是因为在执行代码块后才会检查条件。

函数
"函数"是执行特定任务并可以在程序中重用的代码块。ActionScript 3.0 中有两类函数:"方法"和"函数闭包"。将函数称为方法还是函数闭包取决于定义函数的上下文。如果您将函数定义为类定义的一部分或者将它附加到对象的实例,则该函数称为方法。如果您以其它任何方式定义函数,则该函数称为函数闭包。
函数的基本概念
调用函数
可通过使用后跟小括号运算符 (()) 的函数标识符来调用函数。要发送给函数的任何函数参数都括在小括号中。
如果要调用没有参数的函数,则必须使用一对空的小括号。
定义您自己的函数
在 ActionScript 3.0 中可通过两种方法来定义函数:使用函数语句和使用函数表达式。您可以根据自己的编程风格(偏于静态还是偏于动态)来选择相应的方法。如果您倾向于采用静态或严格模式的编程,则应使用函数语句来定义函数。如果您有特定的需求,需要用函数表达式来定义函数,则应这样做。函数表达式更多地用在动态编程或标准模式编程中。
函数语句
函数语句是在严格模式下定义函数的首选方法。函数语句以 function 关键字开头,后跟:
函数名
用小括号括起来的逗号分隔参数列表
用大括号括起来的函数体 ─ 即,在调用函数时要执行的 ActionScript 代码
函数表达式
声明函数的第二种方法就是结合使用赋值语句和函数表达式,函数表达式有时也称为函数字面值或匿名函数。这是一种较为繁杂的方法,在早期的 ActionScript 版本中广为使用。
带有函数表达式的赋值语句以 var 关键字开头,后跟:
函数名
冒号运算符 (:)
指示数据类型的 Function 类
赋值运算符 (=)
function 关键字
用小括号括起来的逗号分隔参数列表
用大括号括起来的函数体 ─ 即,在调用函数时要执行的 ActionScript 代码
函数表达式和函数语句的另一个重要区别是,函数表达式是表达式,而不是语句。这意味着函数表达式不能独立存在,而函数语句则可以。函数表达式只能用作语句(通常是赋值语句)的一部分。
在函数语句和函数表达式之间进行选择
原则上,除非在特殊情况下要求使用表达式,否则应使用函数语句。函数语句较为简洁,而且与函数表达式相比,更有助于保持严格模式和标准模式的一致性。
函数语句比包含函数表达式的赋值语句更便于阅读。与函数表达式相比,函数语句使代码更为简洁而且不容易引起混淆,因为函数表达式既需要 var 关键字又需要 function 关键字。
函数语句更有助于保持严格模式和标准模式的一致性,因为在这两种编译器模式下,均可以借助点语法来调用使用函数语句声明的方法。但这对于用函数表达式声明的方法却不一定成立。
一般认为,函数表达式更适合于关注运行时行为或动态行为的编程。如果您喜欢使用严格模式,但是还需要调用使用函数表达式声明的方法,则可以使用这两种方法中的任一方法。
首先,可以使用中括号 ([ ]) 代替点运算符 ( . ) 来调用该方法。
第二,您可以将整个类声明为动态类。尽管这样您就可以使用点运算符来调用方法,但缺点是,该类的所有实例在严格模式下都将丢失一些功能。例如,如果您尝试访问动态类实例的未定义属性,则编译器不生成错误。
函数语句与函数表达式之间有两个细微的区别,在选择要使用的方法时,应考虑这两个区别。
第一个区别体现在内存管理和垃圾回收方面,因为函数表达式不像对象那样独立存在。换言之,当您将某个函数表达式分配给另一个对象(如数组元素或对象属性)时,就会在代码中创建对该函数表达式的唯一引用。如果该函数表达式所附加到的数组或对象脱离作用域或由于其它原因不再可用,您将无法再访问该函数表达式。如果删除该数组或对象,该函数表达式所使用的内存将符合垃圾回收条件,这意味着内存符合回收条件并且可重新用于其它用途。
函数语句与函数表达式之间的第二个区别是,函数语句存在于定义它们的整个作用域(包括出现在该函数语句前面的语句)内。与之相反,函数表达式只是为后续的语句定义的。函数表达式只有在定义之后才可用。
从函数中返回值
要从函数中返回值,请使用后跟要返回的表达式或字面值的 return 语句。
请注意,return 语句会终止该函数,因此,不会执行位于 return 语句下面的任何语句。
在严格模式下,如果您选择指定返回类型,则必须返回相应类型的值。
嵌套函数
您可以嵌套函数,这意味着函数可以在其它函数内部声明。除非将对嵌套函数的引用传递给外部代码,否则嵌套函数将仅在其父函数内可用。
在将嵌套函数传递给外部代码时,它们将作为函数闭包传递,这意味着嵌套函数保留在定义该函数时处于作用域内的任何定义。
函数参数
按值或按引用传递参数
在许多编程语言中,一定要了解按值传递参数与按引用传递参数之间的区别,二者之间的区别会影响代码的设计方式。
按值传递意味着将参数的值复制到局部变量中以便在函数内使用。按引用传递意味着将只传递对参数的引用,而不传递实际值。这种方式的传递不会创建实际参数的任何副本,而是会创建一个对变量的引用并将它作为参数传递,并且会将它赋给局部变量以便在函数内部使用。局部变量是对函数外部的变量的引用,它使您能够更改初始变量的值。
在 ActionScript 3.0 中,所有的参数均按引用传递,因为所有的值都存储为对象。但是,属于基元数据类型(包括 Boolean、Number、int、uint 和 String)的对象具有一些特殊运算符,这使它们可以像按值传递一样工作。例如,下面的代码创建一个名为 passPrimitives() 的函数,该函数定义了两个类型均为 int、名称分别为 xParam 和 yParam 的参数。这些参数与在 passPrimitives() 函数体内声明的局部变量类似。当使用 xValue 和 yValue 参数调用函数时,xParam 和 yParam 参数将用对 int 对象的引用进行初始化,int 对象由 xValue 和 yValue 表示。因为参数是基元值,所以它们像按值传递一样工作。尽管 xParam 和 yParam 最初仅包含对 xValue 和 yValue 对象的引用,但是,对函数体内的变量的任何更改都会导致在内存中生成这些值的新副本。
function passPrimitives(xParam:int, yParam:int):void
{
xParam++;
yParam++;
trace(xParam, yParam);
}

var xValue:int = 10;
var yValue:int = 15;
trace(xValue, yValue); // 10 15
passPrimitives(xValue, yValue); // 11 16
trace(xValue, yValue); // 10 15
在 passPrimitives() 函数内部,xParam 和 yParam 的值递增,但这不会影响 xValue 和 yValue 的值,如上一条 trace 语句所示。即使参数的命名与 xValue 和 yValue 变量的命名完全相同也是如此,因为函数内部的 xValue 和 yValue 将指向内存中的新位置,这些位置不同于函数外部同名的变量所在的位置。
其它所有对象(即不属于基元数据类型的对象)始终按引用传递,这样您就可以更改初始变量的值。例如,下面的代码创建一个名为 objVar 的对象,该对象具有两个属性:x 和 y。该对象作为参数传递给 passByRef() 函数。因为该对象不是基元类型,所以它不但按引用传递,而且还保持一个引用。这意味着对函数内部的参数的更改将会影响到函数外部的对象属性。
function passByRef(objParam:Object):void
{
objParam.x++;
objParam.y++;
trace(objParam.x, objParam.y);
}
var objVar:Object = {x:10, y:15};
trace(objVar.x, objVar.y); // 10 15
passByRef(objVar); // 11 16
trace(objVar.x, objVar.y); // 11 16
objParam 参数与全局 objVar 变量引用相同的对象。正如在本示例的 trace 语句中所看到的一样,对 objParam 对象的 x 和 y 属性所做的更改将反映在 objVar 对象中。
默认参数值
ActionScript 3.0 中新增了为函数声明"默认参数值"的功能。如果在调用具有默认参数值的函数时省略了具有默认值的参数,那么,将使用在函数定义中为该参数指定的值。所有具有默认值的参数都必须放在参数列表的末尾。指定为默认值的值必须是编译时常量。如果某个参数存在默认值,则会有效地使该参数成为"可选参数"。没有默认值的参数被视为"必需的参数"。
arguments 对象
在将参数传递给某个函数时,可以使用 arguments 对象来访问有关传递给该函数的参数的信息。arguments 对象的一些重要方面包括:
arguments 对象是一个数组,其中包括传递给函数的所有参数。
arguments.length 属性报告传递给函数的参数数量。
arguments.callee 属性提供对函数本身的引用,该引用可用于递归调用函数表达式。
如果将任何参数命名为 arguments,或者使用 ...(rest) 参数,则 arguments 对象不可用。
在 ActionScript 3.0 中,函数调用中所包括的参数的数量可以大于在函数定义中所指定的参数数量,但是,如果参数的数量小于必需参数的数量,在严格模式下将生成编译器错误。您可以使用 arguments 对象的数组样式来访问传递给函数的任何参数,而无需考虑是否在函数定义中定义了该参数。
arguments.callee 属性通常用在匿名函数中以创建递归。您可以使用它来提高代码的灵活性。如果递归函数的名称在开发周期内的不同阶段会发生改变,而且您使用的是 arguments.callee(而非函数名),则不必花费精力在函数体内更改递归调用。
如果您在函数声明中使用 ...(rest) 参数,则不能使用 arguments 对象,而必须使用为参数声明的参数名来访问参数。还应避免将 "arguments" 字符串作为参数名,因为它将遮蔽 arguments 对象。
...(rest) 参数
ActionScript 3.0 中引入了一个称为 ...(rest) 参数的新参数声明。此参数可用来指定一个数组参数以接受任意多个以逗号分隔的参数。此参数可以拥有保留字以外的任意名称。此参数声明必须是最后一个指定的参数。使用此参数会使 arguments 对象变得不可用。尽管 ...(rest) 参数提供了与 arguments 数组和 arguments.length 属性相同的功能,但是它不提供与 arguments.callee 类似的功能。使用 ...(rest) 参数之前,应确保不需要使用 arguments.callee。
...(rest) 参数还可与其它参数一起使用,前提是它是最后一个列出的参数。并且其它参数不再属于由 ...(rest) 参数创建的数组。
函数作为对象
ActionScript 3.0 中的函数是对象。当您创建函数时,就是在创建对象,该对象不仅可以作为参数传递给另一个函数,而且还可以有附加的属性和方法。
作为参数传递给另一个函数的函数是按引用(而不是按值)传递的。在将某个函数作为参数传递时,只能使用标识符,而不能使用在调用方法时所用的小括号运算符。
尽管刚接触 ActionScript 的程序员可能对此感觉有些奇怪,但是,函数确实可以像其它任何对象那样具有属性和方法。实际上,每个函数都有一个名为 length 的只读属性,它用来存储为该函数定义的参数数量。该属性与 arguments.length 属性不同,后者报告发送给函数的参数数量。回想一下,在 ActionScript 中,发送给函数的参数数量可以超过为该函数定义的参数数量。
您可以定义自己的函数属性,方法是在函数体外部定义它们。函数属性可以用作准静态属性,用来保存与该函数有关的变量的状态。
函数作用域
函数的作用域不但决定了可以在程序中的什么位置调用函数,而且还决定了函数可以访问哪些定义。适用于变量标识符的作用域规则同样也适用于函数标识符。在全局作用域中声明的函数在整个代码中都可用。嵌套函数(即在另一个函数中声明的函数)可以用在声明它的函数中的任意位置。
作用域链
无论何时开始执行函数,都会创建许多对象和属性。首先,会创建一个称为"激活对象"的特殊对象,该对象用于存储在函数体内声明的参数以及任何局部变量或函数。由于激活对象属于内部机制,因此您无法直接访问它。接着,会创建一个"作用域链",其中包含由 Flash Player 检查标识符声明的对象的有序列表。所执行的每个函数都有一个存储在内部属性中的作用域链。对于嵌套函数,作用域链始于其自己的激活对象,后跟其父函数的激活对象。作用域链以这种方式延伸,直到到达全局对象。全局对象是在 ActionScript 程序开始时创建的,其中包含所有的全局变量和函数。
函数闭包
"函数闭包"是一个对象,其中包含函数的快照及其"词汇环境"。函数的词汇环境包括函数作用域链中的所有变量、属性、方法和对象以及它们的值。无论何时在对象或类之外的位置执行函数,都会创建函数闭包。函数闭包保留定义它们的作用域,这样,在将函数作为参数或返回值传递给另一个作用域时,会产生有趣的结果。
例如,下面的代码创建两个函数:foo()(返回一个用来计算矩形面积的嵌套函数 rectArea())和 bar()(调用 foo() 并将返回的函数闭包存储在名为 myProduct 的变量中)。即使 bar() 函数定义了自己的局部变量 x(值为 2),当调用函数闭包 myProduct() 时,该函数闭包仍保留在函数 foo() 中定义的变量 x(值为 40)。因此,bar() 函数将返回值 160,而不是 8。
function foo():Function
{
var x:int = 40;
function rectArea(y:int):int // 定义函数闭包
{
return x * y
}
return rectArea;
}
function bar():void
{
var x:int = 2;
var y:int = 4;
var myProduct:Function = foo();
trace(myProduct(4)); // 调用函数闭包
}
bar(); // 160
方法的行为与函数闭包类似,因为方法也保留有关创建它们的词汇环境的信息。当方法提取自它的实例(这会创建绑定方法)时,此特征尤为突出。函数闭包与绑定方法之间的主要区别在于,绑定方法中 this 关键字的值始终引用它最初附加到的实例,而函数闭包中 this 关键字的值可以改变。




ActionScript 中面向对象的编程
面向对象的编程基础知识
面向对象的编程简介
面向对象的编程 (OOP) 是一种组织程序代码的方法,它将代码划分为对象,即包含信息(数据值)和功能的单个元素。通过使用面向对象的方法来组织程序,您可以将特定信息及其关联的通用功能或动作组合在一起。这些项目将合并为一个项目,即对象。能够将这些值和功能捆绑在一起会带来很多好处,其中包括只需跟踪单个变量而非多个变量、将相关功能组织在一起,以及能够以更接近实际情况的方式构建程序。
常见的面向对象编程任务
实际上,面向对象的编程包含两个部分。一部分是程序设计策略和技巧(通常称为"面向对象的设计")。这是一个很广泛的主题,本章中不对其进行讨论。OOP 的另一部分是在给定编程语言中提供的实际编程结构,以便使用面向对象的方法来构建程序。本章介绍了 OOP 中的以下常见任务:
定义类
创建属性、方法以及 get 和 set 存取器(存取器方法)
控制对类、属性、方法和存取器的访问
创建静态属性和方法
创建与枚举类似的结构
定义和使用接口
处理继承(包括覆盖类元素)
重要概念和术语
属性 (Attribute):在类定义中为类元素(如属性或方法)分配的特性。属性通常用于定义程序的其它部分中的代码能否访问属性或方法。例如,private 和 public 都是属性。私有方法只能由类中的代码调用;而公共方法可以由程序中的任何代码调用。
类 (Class):某种类型的对象的结构和行为定义(与该数据类型的对象的模板或蓝图类似)。
类层次结构 (Class hierarchy):多个相关的类的结构,用于指定哪些类继承了其它类中的功能。
构造函数 (Constructor):可以在类中定义的特殊方法,创建类的实例时将调用该方法。构造函数通常用于指定默认值,或以其它方式执行对象的设置操作。
数据类型 (Data type):特定变量可以存储的信息类型。通常,"数据类型"表示与"类"相同的内容。
点运算符 (Dot operator):句点符号 (.),在 ActionScript(和很多其它编程语言)中,它用于指示某个名称引用对象的子元素(如属性或方法)。
枚举 (Enumeration):一组相关常数值,为方便起见而将其作为一个类的属性组合在一起。
继承 (Inheritance):一种 OOP 机制,它允许一个类定义包含另一个类定义的所有功能(通常会添加到该功能中)。
实例 (Instance):在程序中创建的实际对象。
命名空间 (Namespace):实质上是一个自定义属性,它可以更精确地控制代码对其它代码的访问。

类是对象的抽象表示形式。类用来存储有关对象可保存的数据类型及对象可表现的行为的信息。
类定义
正确的类定义语法中要求 class 关键字后跟类名。类体要放在大括号 ({}) 内,且放在类名后面。
在 ActionScript 3.0 中,引入了 package 语句,包名称必须包含在包声明中,而不是包含在类声明中。
类属性
在 ActionScript 3.0 中,可使用以下四个属性之一来修改类定义:
属性
定义
dynamic
允许在运行时向实例添加属性。
final
不得由其它类扩展。
internal(默认)
对当前包内的引用可见。
公共
对所有位置的引用可见。
使用 internal 以外的每个属性时,必须显式包含该属性才能获得相关的行为。例如,如果定义类时未包含 dynamic 属性 (attribute),则不能在运行时向类实例中添加属性 (property)。通过在类定义的开始处放置属性,可显式地分配属性。
请注意,列表中未包含名为 abstract 的属性。这是因为 ActionScript 3.0 不支持抽象类。同时还请注意,列表中未包含名为 private 和 protected 的属性。这些属性只在类定义中有意义,但不可以应用于类本身。
如果不希望某个类在包以外公开可见,请将该类放在包中,并用 internal 属性标记该类。或者,可以省略 internal 和 public 这两个属性,编译器会自动为您添加 internal 属性。如果不希望某个类在定义该类的源文件以外可见,请将类放在包定义右大括号下面的源文件底部。
类体
类体放在大括号内,用于定义类的变量、常量和方法。还可以在类体中定义命名空间。
ActionScript 3.0 不但允许在类体中包括定义,而且还允许包括语句。如果语句在类体中但在方法定义之外,这些语句只在第一次遇到类定义并且创建了相关的类对象时执行一次。
ActionScript 3.0 中允许在同一类体中定义同名的静态属性和实例属性。
类属性 (property) 的属性 (attribute)
讨论 ActionScript 对象模型时,术语"属性"指可以成为类成员的任何成员,包括变量、常量和方法。这与《ActionScript 3.0 语言和组件参考》中该术语的使用方式有所不同,后者中该术语的使用范围更窄,只包括作为变量的类成员或用 getter 或 setter 方法定义的类成员。在 ActionScript 3.0 中,提供了可以与类的任何属性 (property) 一起使用的一组属性 (attribute)。下表列出了这组属性。
属性
定义
internal(默认)
对同一包中的引用可见。
private
对同一类中的引用可见。
protected
对同一类及派生类中的引用可见。
public
对所有位置的引用可见。
static
指定某一属性属于该类,而不属于该类的实例。
UserDefinedNamespace
用户定义的自定义命名空间名。
访问控制命名空间属性
ActionScript 3.0 提供了四个特殊的属性 (attribute) 来控制对在类中定义的属性 (property) 的访问:public、private、protected 和 internal。
使用 public 属性 (attribute) 可使某一属性 (property) 在脚本的任何位置可见。例如,要使某个方法可用于包外部的代码,必须使用 public 属性声明该方法。这适用于任何属性,不管属性是使用 var、const 还是 function 关键字声明的。
使用 private 属性 (attribute) 可使某一属性 (property) 只对属性 (property) 的定义类中的调用方可见。
在 ActionScript 3.0 中使用严格模式时,尝试使用点运算符访问私有属性会导致编译时错误。否则,会在运行时报告错误,就像使用属性访问运算符时一样。
下表汇总了试图访问属于密封(非动态)类的 private 属性的结果:

严格模式
标准模式
点运算符 (.)
编译时错误
运行时错误
中括号运算符 ([])
运行时错误
运行时错误
在使用 dynamic 属性声明的类中尝试访问私有变量时,不会导致运行时错误。只是变量不可见,所以 Flash Player 返回值 undefined。但是,如果在严格模式下使用点运算符,则会发生编译时错误。
当类外部的代码尝试访问 private 属性时,动态类通常会返回值 undefined,而不是生成错误。
下表说明了只有在严格模式下使用点运算符访问 private 属性时才会生成错误:

严格模式
标准模式
点运算符 (.)
编译时错误
undefined
中括号运算符 ([])
undefined
undefined
protected 属性 (attribute) 是 ActionScript 3.0 中的新增属性 (attribute),可使属性 (property) 对所属类或子类中的调用方可见。换句话说,protected 属性在所属类中可用,或者对继承层次结构中该类下面的类可用。无论子类在同一包中还是在不同包中,这一点都适用。
internal 属性 (attribute) 是 ActionScript 3.0 的新增属性 (attribute),可使属性 (property) 对所在包中的调用方可见。该属性是包中代码的默认属性 (attribute),它适用于没有以下任意属性 (attribute) 的任何属性 (property):
public
private
protected
用户定义的命名空间
static 属性
static 属性 (attribute) 可以与用 var、const 或 function 关键字声明的那些属性 (property) 一起使用,使用该属性 (attribute) 可将属性 (property) 附加到类而不是类的实例。类外部的代码必须使用类名(而不是使用实例名)调用静态属性 (property)。
静态属性不由子类继承的,但是这些属性是子类作用域链中的一部分。这意味着在子类体中,不必引用在其中定义静态变量或方法的类,就可以使用静态变量或方法。
用户定义的命名空间属性
作为预定义访问控制属性的替代方法,您可以创建自定义命名空间以用作属性。每个定义只能使用一个命名空间属性,而且不能将命名空间属性与任何访问控制属性(public、private、protected 和 internal)组合使用。
变量
静态变量
可以使用 var 或 const 关键字声明变量。在脚本的整个执行过程中,使用 var 关键字声明的变量可多次更改其变量值。使用 const 关键字声明的变量称为"常量",只能赋值一次。尝试给已初始化的常量分配新值,将生成错误。
静态变量是使用 static 关键字和 var 或 const 语句共同声明的。静态变量附加到类而不是类的实例,对于存储和共享应用于对象的整个类的信息非常有用。
必须在声明常量的同时初始化使用 static 和 const 关键字声明的变量,您不能为构造函数或实例方法中的静态变量赋值。
实例变量
实例变量包括使用 var 和 const 关键字但未使用 static 关键字声明的属性。实例变量附加到类实例而不是整个类,对于存储特定于实例的值很有用。例如,Array 类有一个名为 length 的实例属性,用来存储 Array 类的特定实例保存的数组元素的个数。
不能覆盖子类中声明为 var 或 const 的实例变量。但是,通过覆盖 getter 和 setter 方法,可以实现类似于覆盖变量的功能。
方法
方法是类定义中的函数。创建类的一个实例后,该实例就会捆绑一个方法。与在类外部声明的函数不同,不能将方法与附加方法的实例分开使用。
方法是使用 function 关键字定义的,或者,也可以使用分配了函数表达式的变量。
多数情况下,您需要使用函数语句而不是函数表达式,原因如下:
函数语句更为简洁易读。
函数语句允许使用 override 和 final 关键字。
函数语句在标识符(即函数名)与方法体代码之间创建了更强的绑定。由于可以使用赋值语句更改
变量值,可随时断开变量与其函数表达式之间的连接。虽然可通过使用 const(不是 var)声明变量来解决这个问题,但这种方法并不是最好的做法,因为这会使代码难以阅读,还会禁止使用 override 和 final 关键字。
必须使用函数表达式的一种情况是:选择将函数附加到原型对象时。

构造函数方法
构造函数方法有时简单称为"构造函数",是与在其中定义函数的类共享同一名称的函数。只要使用 new 关键字创建了类实例,就会执行构造函数方法中包括的所有代码。
构造函数方法只能是公共方法,但可以选择性地使用 public 属性。不能对构造函数使用任何其它访问控制说明符(包括使用 private、protected 或 internal)。也不能对函数构造方法使用用户定义的命名空间。
构造函数可以使用 super() 语句显式地调用其直接超类的构造函数。如果未显式调用超类构造函数,编译器会在构造函数体中的第一个语句前自动插入一个调用。还可以使用 super 前缀作为对超类的引用来调用超类的方法。如果决定在同一构造函数中使用 super() 和 super,务必先调用 super()。否则,super 引用的行为将会与预期不符。另外,super() 构造函数也应在 throw 或 return 语句之前调用。
虽然在构造函数中使用 return 语句是合法的,但是不允许返回值。换句话说,return 语句不得有相关的表达式或值。因此,不允许构造函数方法返回值,这意味着不可以指定任何返回值。
如果没有在类中定义构造函数方法,编译器将会为您自动创建一个空构造函数。如果某个类扩展了另一个类,编译器将会在所生成的构造函数中包括 super() 调用。
静态方法
静态方法也叫做"类方法",它们是使用 static 关键字声明的方法。静态方法附加到类而不是类的实例,因此在封装对单个实例的状态以外的内容有影响的功能时,静态方法很有用。由于静态方法附加到整个类,所以只能通过类访问静态方法,而不能通过类实例访问。
静态方法为封装所提供的功能不仅仅在影响类实例状态的方面。换句话说,如果方法提供的功能对类实例的值没有直接的影响,该方法应是静态方法。
由于静态方法不绑定到单个实例,因此不能在静态方法体中使用关键字 this 或 super。this 和 super 这两个引用只在实例方法上下文中有意义。
与其它基于类的编程语言不同,ActionScript 3.0 中的静态方法不是继承的。
实例方法
实例方法指的是不使用 static 关键字声明的方法。实例方法附加到类实例而不是整个类,在实现对类的各个实例有影响的功能时,实例方法很有用。
在实例方法体中,静态变量和实例变量都在作用域中,这表示使用一个简单的标识符可以引用同一类中定义的变量。虽然在 ActionScript 3.0 中不继承静态属性,但是超类的静态属性在作用域中。
实例方法体中的 this 引用的值是对方法所附加实例的引用。
使用关键字 override 和 final 可以控制实例方法的继承。可以使用 override 属性重新定义继承的方法,以及使用 final 属性禁止子类覆盖方法。
get 和 set 存取器方法
get 和 set 存取器函数还分别称为 getter 和 setter,可以使用这些函数为创建的类提供易于使用的编程接口,并遵循信息隐藏和封装的编程原则。使用 get 和 set 函数可保持类的私有类属性,但允许类用户访问这些属性,就像他们在访问类变量而不是调用类方法。
这种方法的好处是,可避免出现具有不实用名称的传统存取器函数,如 getPropertyName() 和 setPropertyName()。getter 和 setter 的另一个好处是,使用它们可避免允许进行读写访问的每个属性有两个面向公共的函数。
使用 getter 和 setter 函数还可以覆盖从超类继承来的属性,这是使用常规类成员变量时不能做到的。在子类中不能覆盖使用 var 关键字声明的类成员变量。但是,使用 getter 和 setter 函数创建的属性没有此限制。可以对从超类继承的 getter 和 setter 函数使用 override 属性。
绑定方法
绑定方法有时也叫做"闭包方法",就是从它的实例提取的方法。作为参数传递给函数的方法或作为值从函数返回的方法都是绑定方法。在 ActionScript 3.0 中,新增的绑定方法类似于闭包函数,其中保留了词汇环境,即使从其实例中提取出来也是如此。绑定方法与闭包函数之间的主要不同差别是,绑定函数的 this 引用保留到实现方法的实例的链接或绑定。换句话说,绑定方法中的 this 引用总是指向实现方法的原始对象。对于闭包函数,this 引用是通用的,这意味着调用函数时,该引用指向与函数关联的任何对象。
如果使用 this 关键字,了解绑定方法就很重要。重新调用 this 关键字可提供对方法父对象的引用。大多数 ActionScript 程序员都希望 this 关键字总是引用包含方法定义的对象或类。但是,如果不使用方法绑定,并不是总是做到这样。例如,在以前版本的 ActionScript 中,this 引用并不总是引用实现方法的实例。从 ActionScript 2.0 的实例中提取方法后,不但 this 引用不绑定到原始实例,而且实例类的成员变量和方法也不可用。在 ActionScript 3.0 中不存在这样的问题,这是因为将方法当作参数传递时会自动创建绑定方法。绑定方法用于确保 this 关键字总是引用在其中定义了方法的对象或类。
下面的代码定义了名为 ThisTest 的类,该类包含一个名为 foo() 的方法(该方法定义绑定方法)和一个名为 bar() 的方法(该方法返回绑定方法)。类外部的代码创建 ThisTest 类的实例,然后调用 bar() 方法,最后将返回值存储在名为 myFunc 的变量中。
class ThisTest
{
private var num:Number = 3;
function foo():void // 定义的绑定方法
{
trace("foo's this: " + this);
trace("num: " + num);
}
function bar():Function
{
return foo; // 返回的绑定方法
}
}

var myTest:ThisTest = new ThisTest();
var myFunc:Function = myTest.bar();
trace(this); // 输出:[全局对象]
myFunc();
/* 输出:
foo's this: [object ThisTest]
output: num: 3 */
代码的最后两行表明:虽然前一行中的 this 引用指向全局对象,但绑定方法 foo() 中的 this 引用仍然指向 ThisTest 类的实例。另外,存储在 myFunc 变量中的绑定方法仍然可以访问 ThisTest 类的成员变量。如果以上代码在 ActionScript 2.0 中运行,this 引用会匹配,但 num 变量将为 undefined。
绑定方法最值得注意的一种情况是使用事件处理函数,因为 addEventListener() 方法要求将函数或方法作为参数来传递。
类的枚举
枚举"是您创建的一些自定义数据类型,用于封装一小组值。ActionScript 3.0 并不支持具体的枚举工具,不过,您可以使用类或静态常量创建枚举。按照惯例,枚举类是使用 final 属性声明的,因为不需要扩展该类。该类仅由静态成员组成,这表示不创建该类的实例。而是直接通过类对象来访问枚举值。
Flash Player API 中的所有枚举类都只包含 String、int 或 uint 类型的变量。使用枚举而不使用文本字符串或数字值的好处是,使用枚举更易于发现字面错误。如果枚举名输入错误,ActionScript 编译器会生成一个错误。如果使用字面值,存在拼写错误或使用了错误数字时,编译器并不会报错。
创建枚举的第二种方法还包括使用枚举的静态属性创建单独的类。这种方法的不同之处在于每一个静态属性都包含一个类实例,而不是字符串或整数值。
Flash Player API 并不使用这种方法,但是许多开发人员都使用,他们更喜欢使用这种方法提供的改进类型检查功能。
嵌入资源类
ActionScript 3.0 使用称为"嵌入资源类"的特殊类来表示嵌入的资源。"嵌入资源"指的编译时包括在 SWF 文件中的资源,如声音、图像或字体。嵌入资源而不是动态加载资源,可以确保资源在运行时可用,但代价是增加了 SWF 文件的大小。
在 Flash 中使用嵌入资源类
要嵌入资源,首先将该资源放入 FLA 文件的库中。接着,使用资源的链接属性,提供资源的嵌入资源类的名称。如果无法在类路径中找到具有该名称的类,则将自动生成一个类。然后,可以创建嵌入资源类的实例,并使用任何由该类定义或继承的属性和方法。
接口的基础是方法的接口与方法的实现之间的区别。方法的接口包括调用该方法必需的所有信息,包括方法名、所有参数和返回类型。方法的实现不仅包括接口信息,而且还包括执行方法的行为的可执行语句。接口定义只包含方法接口,实现接口的所有类负责定义方法实现。
另一种描述接口的方法是:接口定义了数据类型,就像类一样。因此,接口可以用作类型注释,也像类一样。作为数据类型,接口还可以与需要指定数据类型的运算符一起使用,如 is 和 as 运算符。但是与类不同的是,接口不可以实例化。这个区别使很多程序员认为接口是抽象的数据类型,认为类是具体的数据类型。
定义接口
接口定义的结构类似于类定义的结构,只是接口只能包含方法但不能包含方法体。接口不能包含变量或常量,但是可以包含 getter 和 setter。要定义接口,请使用 interface 关键字。
只能使用 public 和 internal 访问控制说明符来修饰接口定义。接口定义中的方法声明不能包含任何访问控制说明符。
Flash Player API 遵循一种约定,其中接口名以大写 I 开始,但是可以使用任何合法的标识符作为接口名。接口定义经常位于包的顶级。接口定义不能放在类定义或另一个接口定义中。
接口可扩展一个或多个其它接口。
在类中实现接口
类是唯一可实现接口的 ActionScript 3.0 语言元素。在类声明中使用 implements 关键字可实现一个或多个接口。
在实现接口的类中,实现的方法必须:
使用 public 访问控制标识符。
使用与接口方法相同的名称。
拥有相同数量的参数,每一个参数的数据类型都要与接口方法参数的数据类型相匹配。
使用相同的返回类型。
不过在命名所实现方法的参数时,您有一定的灵活性。虽然实现的方法的参数数和每个参数的数据类型必须与接口方法的参数数和数据类型相匹配,但参数名不需要匹配。
另外,使用默认参数值也具有一定的灵活性。接口定义可以包含使用默认参数值的函数声明。实现这种函数声明的方法必须采用默认参数值,默认参数值是与接口定义中指定的值具有相同数据类型的一个成员,但是实际值不一定匹配。
提供这种灵活性的原因是,实现接口的规则的设计目的是确保数据类型兼容性,因此不必要求采用相同的参数名和默认参数名,就能实现目标。
继承
继承是指一种代码重用的形式,允许程序员基于现有类开发新类。现有类通常称为"基类"或"超类",新类通常称为"子类"。继承的主要优势是,允许重复使用基类中的代码,但不修改现有代码。此外,继承不要求改变其它类与基类交互的方式。不必修改可能已经过彻底测试或可能已被使用的现有类,使用继承可将该类视为一个集成模块,可使用其它属性或方法对它进行扩展。因此,您使用 extends 关键字指明类从另一类继承。
通过继承还可以在代码中利用"多态"。有一种方法在应用于不同数据类型时会有不同行为,多态就是对这样的方法应用一个方法名的能力。使用继承能实现多态,实现的方式是允许子类继承和重新定义或"覆盖"基类中的方法。
因为每个类定义一个数据类型,所以使用继承会在基类和扩展基类的类之间创建一个特殊关系。子类保证拥有其基类的所有属性,这意味着子类的实例总是可以替换基类的实例。
实例属性和继承
对于实例属性 (property),无论是使用 function、var 还是使用 const 关键字定义的,只要在基类中未使用 private 属性 (attribute) 声明该属性 (property),这些属性都可以由子类继承。
访问控制说明符和继承
如果某一属性是用 public 关键字声明的,则该属性对任何位置的代码可见。这表示 public 关键字与 private、protected 和 internal 关键字不同,它对属性继承没有任何限制。
如果属性是使用 private 关键字声明的,该属性只在定义该属性的类中可见,这表示它不能由任何子类继承。
protected 关键字指出某一属性不仅在定义该属性的类中可见,而且还在所有子类中可见。ActionScript 3.0 中的 protected 关键字并不使属性对同一包中的所有其它类可见。此外,protected 属性对子类可见,不管子类和基类是在同一包中,还是在不同包中。
要限制某一属性在定义该属性的包中的可见性,请使用 internal 关键字或者不使用任何访问控制说明符。未指定访问控制说明符时,应用的默认访问控制说明符是 internal 访问控制说明符。标记为 internal 的属性将只由位于在同一包中的子类继承。
不允许覆盖变量
将继承使用 var 或 const 关键字声明的属性,但不能对其进行覆盖。覆盖某一属性就表示在子类中重新定义该属性。唯一可覆盖的属性类型是方法,即使用 function 关键字声明的属性。虽然不能覆盖实例变量,但是通过为实例变量创建 getter 和 setter 方法并覆盖这些方法,可实现类似的功能。
覆盖方法
覆盖方法表示重新定义已继承方法的行为。静态方法不能继承,也不能覆盖。但是,实例方法可由子类继承,也可覆盖,只要符合以下两个条件:
实例方法在基类中不是使用 final 关键字声明的。当 final 关键字与实例方法一起使用时,该关键字指明程序员的设计目的是要禁止子类覆盖方法。
实例方法在基类中不是使用 private 访问控制说明符声明的。如果某个方法在基类中标记为 private,则在子类中定义同名方法时不需要使用 override 关键字,因为基类方法在子类中不可见。
要覆盖符合这些条件的实例方法,子类中的方法定义必须使用 override 关键字,且必须在以下几个方面与方法的超类版本相匹配:
覆盖方法必须与基类方法具有相同级别的访问控制。标记为内部的方法与没有访问控制说明符的方法具有相同级别的访问控制。
覆盖方法必须与基类方法具有相同的参数数。
覆盖方法参数必须与基类方法参数具有相同的数据类型注释。
覆盖方法必须与基类方法具有相同的返回类型。
但是,覆盖方法中的参数名不必与基类中的参数名相匹配,只要参数数和每个参数的数据类相匹配即可。
super 语句
覆盖方法时,程序员经常希望在要覆盖的超类方法的行为上添加行为,而不是完全替换该行为。这需要通过某种机制来允许子类中的方法调用它本身的超类版本。super 语句就提供了这样一种机制,其中包含对直接超类的引用。
覆盖 getter 和 setter
虽然不能覆盖超类中定义的变量,但是可以覆盖 getter 和 setter。
不继承静态属性
静态属性不由子类继承。这意味着不能通过子类的实例访问静态属性。只能通过定义静态属性的类对象(即 类本身而非实例)来访问静态属性。
但允许使用与静态属性相同的名称定义实例属性。可以在与静态属性相同的类中或在子类中定义这样的实例属性。
静态属性和作用域链
虽然并不继承静态属性,但是静态属性在定义它们的类或该类的任何子类的作用域链中。同样,可以认为静态属性在定义它们的类和任何子类的"作用域"中。这意味着在定义静态属性的类体及该类的任何子类中可直接访问静态属性。
如果使用与同类或超类中的静态属性相同的名称定义实例属性,则实例属性在作用域链中的优先级比较高。因此认为实例属性"遮蔽"了静态属性,这意味着会使用实例属性的值,而不使用静态属性的值。


高级主题
本节开始先简单介绍 ActionScript 和 OOP 的历史,然后讨论 ActionScript 3.0 对象模型,以及该模型如何启用新的 ActionScript 虚拟机 (AVM2) 显著提供运行速度(与包含旧 ActionScript 虚拟机 (AVM1) 的以前版本的 Flash Player 相比)。
ActionScript OOP 支持的历史
由于 ActionScript 3.0 是在以前版本的 ActionScript 基础上构建的,了解 ActionScript 对象模型的发展过程可能有所帮助。ActionScript 最初作为早期版本的 Flash 创作工具的简单编写脚本机制。后来,程序员开始使用 ActionScript 建立更加复杂的应用程序。为了迎合这些程序员的需要,每个后续版本都添加了一些语言功能以帮助创建复杂的应用程序。
ActionScript 1.0
ActionScript 1.0 指在 Flash Player 6 和更早版本中使用的语言版本。即使在这个早期开发阶段,ActionScript 对象模型也是建立在基础数据类型对象的概念的基础上。ActionScript 对象是由一组"属性"构成的复合数据类型。讨论对象模型时,术语"属性"包括附加到对象的所有内容,如变量、函数或方法。
尽管第一代 ActionScript 不支持使用 class 关键字定义类,但是可以使用称为原型对象的特殊对象来定义类。Java 和 C++ 等基于类的语言中使用 class 关键字创建要实例化为具体对象的抽象类定义,而 ActionScript 1.0 等基于原型的语言则将现有对象用作其它对象的模型(或原型)。基于类的语言中的对象可能指向作为其模板的类,而基于原型的语言中的对象则指向作为其模板的另一个对象(即其原型)。
要在 ActionScript 1.0 中创建类,可以为该类定义一个构造函数。在 ActionScript 中,函数不只是抽象定义,还是实际对象。您创建的构造函数用作该类实例的原型对象。以下代码创建了一个名为 Shape 的类,还定义了一个名为 visible 的属性,该属性默认情况下设置为 true:
// 基类
function Shape() {}
// 创建名为 visible 的属性。
Shape.prototype.visible = true;
此构造函数定义了可以使用 new 运算符实例化的 Shape 类,如下所示:
myShape = new Shape();
就像 Shape() 构造函数对象用作 Shape 类实例的原型一样,它还可以用作 Shape 的子类(即扩展 Shape 类的其它类)的原型。
创建作为 Shape 类的子类的类的过程分两个步骤。首先,通过定义类的构造函数创建该类,如下所示:
// 子类
function Circle(id, radius)
{
this.id = id;
this.radius = radius;
}
然后,使用 new 运算符将 Shape 类声明为 Circle 类的原型。默认情况下,创建的所有类都使用 Object 类作为其原型,这意味着 Circle.prototype 当前包含一个通用对象(Object 类的实例)。要指定 Circle 的原型是 Shape 而不是 Object,请使用以下代码更改 Circle.prototype 的值,使其包含 Shape 对象而不是通用对象。
// 使 Circle 成为 Shape 的子类。
Circle.prototype = new Shape();
Shape 类和 Circle 类现在通过通常所说的"原型链"的继承关系联系在一起。下图说明了原型链中的关系:

每个原型链末端的基类是 Object 类。Object 类包含一个名为 Object.prototype 的静态属性,该属性指向在 ActionScript 1.0 中创建的所有对象的基础原型对象。示例原型链中的下一个对象是 Shape 对象。这是因为从不显式设置 Shape.prototype 属性,所以它仍然包含通用对象(Object 类的实例)。此链中的最后一个链环是 Circle 类,该类链接到其原型 Shape 类(Circle.prototype 属性包含 Shape 对象)。
如果创建了 Circle 类的实例(如下面的示例所示),该实例会继承 Circle 类的原型链:
// 创建 Circle 类的实例。
myCircle = new Circle();
回想一下,我们创建了一个名为 visible 的属性作为 Shape 类的成员。在示例中,visible 属性并不作为 myCircle 对象的一部分,只是 Shape 对象的一个成员,但以下代码行的输出为 true:
trace(myCircle.visible); // 输出:true
Flash Player 能够沿着原型链检查 myCircle 对象是否继承了 visible 属性。执行此代码时,Flash Player 首先在 myCircle 对象的属性中搜索名为 visible 的属性,但是未发现这样的属性。Flash Player 然后在下一个 Circle.prototype 对象中查找,但是仍未发现名为 visible 的属性。继续检查原型链,Flash Player 最终发现了在 Shape.prototype 对象上定义的 visible 属性,并输出该属性的值。
为了简便,本节省略了原型链的很多难懂之处和细节,目的是为了提供足够的信息帮助您了解 ActionScript 3.0 对象模型。
ActionScript 2.0
在 ActionScript 2.0 中引入了 class、extends、public 和 private 等新关键字,通过使用这些关键字,您可以按 Java 和 C++ 等基于类的语言用户所熟悉的方式来定义类。ActionScript 1.0 与 ActionScript 2.0 之间的基本继承机制并没有改变,了解这一点很重要。ActionScript 2.0 中只是添加了用于定义类的新语法。在该语言的两个版本中,原型链的作用方式是一样的。
ActionScript 2.0 中引入了新语法(如以下摘录中所示),可允许以数程序员认为更直观的方式定义类:
// 基类
class Shape
{
var visible:Boolean = true;
}
注意,ActionScript 2.0 还引入了用于编译时类型检查的类型注释。使用类型注释,可以将上个示例中的 visible 属性声明为应只包含布尔值。新 extends 关键字还简化了创建子类的过程。在下面的示例中,通过使用 extends 关键字,可以一步完成在 ActionScript 1.0 中需要分两步完成的过程:
// 子类
class Circle extends Shape
{
var id:Number;
var radius:Number;
function Circle(id, radius)
{
this.id = id;
this.radius = radius;
}
}
构造函数现在声明为类定义的一部分,还必须显式声明类属性 id 和 radius。
ActionScript 2.0 中还增加了对接口定义的支持,以使您能够使用为对象间通信正式定义的协议来进一步改进面向对象的程序。
ActionScript 3.0 类对象
常见的面向对象的编程范例多数常与 Java 和 C++ 相关联,这种范例使用类定义对象的类型。采用这种范例的编程语言也趋向使用类来构造类定义的数据类型的实例。ActionScript 使用类是为了实现以上两个目的,但其根本还是一种基于原型的语言,并带有有趣的特征。ActionScript 为每一类定义创建了特殊的类对象,允许共享行为和状态。但是,对多数 ActionScript 程序员而言,这个特点可能与实际编码没有什么牵连。ActionScript 3.0 的设计目的是,不使用(甚至是不必了解)这些特殊类对象,就可创建复杂的面向对象的 ActionScript 应用程序。对于要利用类对象的高级程序员,本节提供有关问题的深入讨论。
下图显示一个类对象的结构,该类对象表示使用语句 class A {} 定义的名为 A 的简单类:

图中的每个矩形表示一个对象。图中的每一个对象都有下标字符 A,这表示该对象属于类 A。类对象 (CA) 包含对许多其它重要对象的引用。实例 traits 对象 (TA) 用于存储在类定义中定义的实例属性。类 traits 对象 (TCA) 表示类的内部类型,用于存储该类定义的静态属性(下标字符 C 代表"类")。原型对象 (PA) 始终指的是最初通过 constructor 属性附加到的类对象。
traits 对象
traits 对象是 ActionScript 3.0 中的新增对象,它是为了提高性能而实现的。在以前版本的 ActionScript 中,名称查找是一个耗时的过程,因为 Flash Player 要搜索原型链。在 ActionScript 3.0 中,名称查找更有效、耗时更少,因为可以将继承属性从超类复制到子类的 traits 对象。
编程代码不能直接访问 traits 对象,但是性能和内存使用情况的改善可反映它的存在。traits 对象给 AVM2 提供了关于类的布局和内容的详细信息。借助这些信息,AVM2 可显著减少执行时间,因为它可以经常生成直接机器指令来直接访问属性或直接调用方法,而省去了查找名称所耗费的时间。
由于使用了 traits 对象,与以前版本中 ActionScript 类似对象相比,该版本中对象占用内存的时间明显减少。例如,如果某个类已密封(即,该类未声明为 dynamic),则该类实例不需要动态添加属性的哈希表,只保留一个到 traits 对象的指针和该类中定义的固定属性的某些位置。因此,如果对象在 ActionScript 2.0 中需要占用 100 个字节的内存,在 ActionScript 3.0 中只需要占用 20 个字节的内存。
traits 对象是内部实现详细信息,不保证在将来版本的 ActionScript 中此对象不更改,甚至消失。
原型对象
每个 ActionScript 类对象都有一个名为 prototype 属性,它表示对类的原型对象的引用。 ActionScript 根本上是基于原型的语言,原型对象是旧内容。
prototype 属性是只读属性,这表示不能将其修改为指向其它对象。这不同于以前版本 ActionScript 中的类 prototype 属性,在以前版本中可以重新分配 prototype,使它指向其它类。虽然 prototype 属性是只读属性,但是它所引用的原型对象不是只读的。换句话说,可以向原型对象添加新属性。向原型对象添加的属性可在类的所有实例中共享。
原型链是以前版本的 ActionScript 中的唯一继承机制,在 ActionScript 3.0 中只充当一个辅助角色。主要的继承机制固定属性继承由 traits 对象内部处理。固定属性指的是定义为类定义的一部分的变量或方法。固定属性继承也叫做类继承,因为它是与 class、extends 和 override 等关键字相关的继承机制。
原型链提供了另一种继承机制,该机制的动态性比固定属性继承的更强。既可以将属性作为类定义的一部分,也可以在运行时通过类对象的 prototype 属性向类的原型对象中添加属性。但是,请注意,如果将编译器设置为严格模式,则不能访问添加到原型对象中的属性,除非使用 dynamic 关键字声明类。
Object 类就是这样类的示例,它的原型对象附加了若干属性。Object 类的 toString() 和 valueOf() 方法实际上是一些函数,它们分配给 Object 类原型对象的属性。以下是一个示例,说明这些方法的声明理论上是怎样的(实际实现时会因实现详细信息而稍有不同):
public dynamic class Object
{
prototype.toString = function()
{
// 语句
};
prototype.valueOf = function()
{
// 语句
};
}
正如前面提到的那样,可以将属性附加到类定义外部的类原型对象。例如,也可以在 Object 类定义外部定义 toString() 方法,如下所示:
Object.prototype.toString = function()
{
// 语句
};
但是,原型继承与固定属性继承不一样,如果要重新定义子类中的方法,原型继承不需要 override 关键字。例如,如果要重新定义 Object 类的子类中的 valueOf() 方法,您有以下三种选择。第一,可以在类定义中的子类原型对象上定义 valueOf() 方法。以下代码创建一个名为 Foo 的 Object 子类,还将 Foo 原型对象的 valueOf() 方法重新定义为类定义的一部分。因为每个类都是从 Object 继承的,所以不需要使用 extends 关键字。
dynamic class Foo
{
prototype.valueOf = function()
{
return "Instance of Foo";
};
}
第二,可以在类定义外部对 Foo 原型对象定义 valueOf() 方法,如以下代码中所示:
Foo.prototype.valueOf = function()
{
return "Instance of Foo";
};
第三,可以将名为 valueOf() 的固定属性定义为 Foo 类的一部分。这种方法与其它混合了固定属性继承与原型继承的方法有所不同。要重新定义 valueOf() 的 Foo 的任何子类必须使用 override 关键字。以下代码显示 valueOf() 定义为 Foo 中的固定属性:
class Foo
{
function valueOf():String
{
return "Instance of Foo";
}
}
AS3 命名空间
由于存在两种继承机制,即固定属性继承和原型继承,所以涉及到核心类的属性和方法时,就存在两种机制的兼容性问题。如果与 ECMAScript 第 4 版语言规范草案兼容,则要求使用原型继承,这意味着核心类的属性和方法是在该类的原型对象上定义的。另一方面,如果与 Flash Player API 兼容,则要求使用固定属性继承,这意味着核心类的属性和方法是使用 const、var 和 function 关键字在类定义中定义的。此外,如果使用固定属性而不是原型属性,将显著提升运行时性能。
在 ActionScript 3.0 中,通过同时将原型继承和固定属性继承用于核心类,解决了这个问题。每一个核心类都包含两组属性和方法。一组是在原型对象上定义的,用于与 ECMAScript 规范兼容,另一组使用固定属性定义和 AS3 命名空间定义,以便与 Flash Player API 兼容。
AS3 命名空间提供了一种约定机制,用来在两组属性和方法之间做出选择。如果不使用 AS3 命名空间,核心类的实例会继承在核心类的原型对象上定义的属性和方法。如果决定使用 AS3 命名空间,核心类的实例会继承 AS3 版本,因为固定属性的优先级始终高于原型属性。换句话说,只要固定属性可用,则始终使用固定属性,而不使用同名的原型属性。
通过用 AS3 命名空间限定属性或方法,可以选择使用 AS3 命名空间版本的属性或方法。例如,下面的代码使用 AS3 版本的 Array.pop() 方法:
var nums:Array = new Array(1, 2, 3);
nums.AS3::pop();
trace(nums); // 输出:1,2
或者,也可以使用 use namespace 指令打开代码块中所有定义的 AS3 命名空间。例如,以下代码使用 use namespace 指令打开 pop() 和 push() 方法的 AS3 命名空间:
use namespace AS3;

var nums:Array = new Array(1, 2, 3);
nums.pop();
nums.push(5);
trace(nums) // 输出:1,2,5
ActionScript 3.0 还为每组属性提供了编译器选项,以便将 AS3 命名空间应用于整个程序。-as3 编译器选项表示 AS3 命名空间,-es 编译器选项表示原型继承选项(es 代表 ECMAScript)。要打开整个程序的 AS3 命名空间,请将 -as3 编译器选项设置为 true,将 -es 编译器选项设置为 false。要使用原型版本,请将编译器选项设置为相反值。Adobe Flex Builder 2 和 Adobe Flash CS3 Professional 的默认编译器选项是 -as3 = true 和 -es = false。
如果计划扩展任何核心类并覆盖任何方法,应了解 AS3 命名空间对声明覆盖方法的方式有什么影响。如果要使用 AS3 命名空间,覆盖核心类方法的任何方法都必须使用 AS3 命名空间以及 override 属性。如果不打算使用 AS3 命名空间且要重新定义子类中的核心类方法,则不应使用 AS3 命名空间或 override 关键字。
处理日期和时间
ActionScript 3.0 提供了多种强大的手段来管理日历日期、时间和时间间隔。以下两个主类提供了大部分的计时功能:Date 类和 flash.utils 包中的新 Timer 类。
日期和时间基础知识
处理日期和时间简介
在 ActionScript 中,可以使用 Date 类来表示某一时刻,其中包含日期和时间信息。Date 实例中包含各个日期和时间单位的值,其中包括年、月、日、星期、小时、分钟、秒、毫秒以及时区。对于更高级的用法,ActionScript 还包括 Timer 类,您可以使用该类在一定延迟后执行动作,或按重复间隔执行动作。
重要概念和术语
UTC 时间 (UTC time):通用协调时间,即"零小时"基准时区。所有其它时区均被定义为相对于 UTC 时间快
或慢一定的小时数。
管理日历日期和时间
创建 Date 对象
Date 类是所有核心类中构造函数方法形式最为多变的类之一。您可以用以下四种方式来调用 Date 类。
第一,如果未给定参数,则 Date() 构造函数将按照您所在时区的本地时间返回包含当前日期和时间的 Date 对象。
第二,如果仅给定了一个数字参数,则 Date() 构造函数将其视为自 1970 年 1 月 1 日以来经过的毫秒数,并且返回对应的 Date 对象。请注意,您传入的毫秒值将被视为自 1970 年 1 月 1 日(UTC 时间)以来经过的毫秒数。但是,该 Date 对象会按照您所在的本地时区来显示值,除非您使用特定于 UTC 的方法来检索和显示这些值。如果仅使用一个毫秒参数来创建新的 Date 对象,则应确保考虑到您的当地时间和 UTC 之间的时区差异。
第三,您可以将多个数值参数传递给 Date() 构造函数。该构造函数将这些参数分别视为年、月、日、小时、分钟、秒和毫秒,并将返回一个对应的 Date 对象。
第四,您可以将单个字符串参数传递给 Date() 构造函数。该构造函数将尝试把字符串解析为日期或时间部分,然后返回对应的 Date 对象。如果您使用此方法,最好将 Date() 构造函数包含在 try..catch 块中以捕获任何解析错误。
如果 Date() 构造函数无法成功解析该字符串参数,它将不会引发异常。但是,所得到的 Date 对象将包含一个无效的日期值。
获取时间单位值
可以使用 Date 类的属性或方法从 Date 对象中提取各种时间单位的值。下面的每个属性为您提供了 Date 对象中的一个时间单位的值:
fullYear 属性
month 属性,以数字格式表示,分别以 0 到 11 表示一月到十二月
date 属性,表示月中某一天的日历数字,范围从 1 到 31
day 属性,以数字格式表示一周中的某一天,其中 0 表示星期日
hours 属性,范围从 0 到 23
minutes 属性
seconds 属性
milliseconds 属性
实际上,Date 类为您提供了获取这些值的多种方式。
执行日期和时间运算
您可以使用 Date 类对日期和时间执行加法和减法运算。日期值在内部以毫秒的形式保存,因此您应将其它值转换成毫秒,然后再将它们与 Date 对象进行加减。
如果应用程序将执行大量的日期和时间运算,您可能会发现创建常量来保存常见时间单位值(以毫秒的形式)非常有用,如下所示:
public static const millisecondsPerMinute:int = 1000 * 60;
public static const millisecondsPerHour:int = 1000 * 60 * 60;
public static const millisecondsPerDay:int = 1000 * 60 * 60 * 24;
设置日期值的另一种方式是仅使用一个毫秒参数创建新的 Date 对象。
在时区之间进行转换
在需要将日期从一种时区转换成另一种时区时,使用日期和时间运算十分方便。也可以使用 getTimezoneOffset() 方法,该方法返回的值表示 Date 对象的时区与 UTC 之间相差的分钟数。此方法之所以返回以分钟为单位的值是因为并不是所有时区之间都正好相差一个小时,有些时区与邻近的时区仅相差半个小时。
控制时间间隔
循环与计时器之比较
在某些编程语言中,您必须使用循环语句(如 for 或 do..while)来设计您自己的计时方案。
通常,循环语句会以本地计算机所允许的速度尽可能快地执行,这表明应用程序在某些计算机上的运行速度较快而在其它计算机上则较慢。如果应用程序需要一致的计时间隔,则您需要将其与实际的日历或时钟时间联系在一起。许多应用程序(如游戏、动画和实时控制器)需要在不同计算机上均能保持一致的、规则的时间驱动计时机制。
ActionScript 3.0 的 Timer 类提供了一个功能强大的解决方案。使用 ActionScript 3.0 事件模型,Timer 类在每次达到指定的时间间隔时都会调度计时器事件。
Timer 类
在 ActionScript 3.0 中处理计时函数的首选方式是使用 Timer 类 (flash.utils.Timer),可以使用它在每次达到间隔时调度事件。
要启动计时器,请先创建 Timer 类的实例,并告诉它每隔多长时间生成一次计时器事件以及在停止前生成多少次事件。
例如,下列代码创建一个每秒调度一个事件且持续 60 秒的 Timer 实例:
var oneMinuteTimer:Timer = new Timer(1000, 60);
Timer 对象在每次达到指定的间隔时都会调度 TimerEvent 对象。TimerEvent 对象的事件类型是 timer(由常量 TimerEvent.TIMER 定义)。TimerEvent 对象包含的属性与标准 Event 对象包含的属性相同。
如果将 Timer 实例设置为固定的间隔数,则在达到最后一次间隔时,它还会调度 timerComplete 事件(由常量 TimerEvent.TIMER_COMPLETE 定义)。
处理字符串
String 类包含使您能够使用文本字符串的方法。字符串是字符的序列。ActionScript 3.0 支持 ASCII 字符和 Unicode 字符。
字符串基础知识
处理字符串简介
在编程语言中,字符串是指一个文本值,即串在一起而组成单个值的一系列字母、数字或其它字符。ActionScript String 类是一种可用来处理文本值的数据类型。
在 ActionScript 中,可使用双引号或单引号将本文引起来以表示字符串值。
重要概念和术语
以下参考列表包含您将会在本章中遇到的重要术语:
ASCII:用于在计算机程序中表示文本字符和符号的系统。ASCII 系统支持 26 个字母英文字母表,以及有限的一组其它字符。
字符 (Character):文本数据的最小单位(单个字母或符号)。
连接 (Concatenation):通过将一个字符串值添加到另一个字符串值结尾,将多个字符串值连接在一起,从而创建一个新的字符串值。
空字符串 (Empty string):不包含任何文本、空白或其它字符的字符串,可以写为 ""。空字符串值不同于具有空值的 String 变量;空 String 变量是指没有赋予 String 实例的变量,而空字符串则包含一个实例,其值不包含任何字符。
字符串 (String):一个文本值(字符序列)。
字符串文本或文本字符串(String literal 或 literal string):在代码中显式编写的字符串值,编写为用双引号或单引号引起来的文本值。
子字符串 (Substring):作为另一个字符串一部分的字符串。
Unicode:用于在计算机程序中表示文本字符和符号的标准系统。Unicode 系统允许使用任何编写系统中的任何字符。
创建字符串
在 ActionScript 3.0 中,String 类用于表示字符串(文本)数据。ActionScript 字符串既支持 ASCII 字符也支持 Unicode 字符。
创建字符串的最简单方式是使用字符串文本。要声明字符串文本,请使用双直引号(")或单直引号(')字符。
您还可以使用 new 运算符来声明字符串。
下面的两个字符串是等效的。
var str1:String = "hello";
var str2:String = new String("hello");
要在使用单引号 (') 分隔符定义的字符串文本内使用单引号 ('),请使用反斜杠转义符 ()。类似地,要在使用双引号 (") 分隔符定义的字符串文本内使用双引号 ("),请使用反斜杠转义符 ()。
您可以根据字符串文本中存在的任何单引号或双引号来选择使用单引号或双引号,如下所示:
var str1:String = "ActionScript <span class='heavy'>3.0</span>";
var str2:String = '<item id="155">banana</item>';
请务必记住 ActionScript 可区分单直引号 (') 和左右单引号 (' 或 ')。对于双引号也同样如此。请使用直引号来分割字符串文本。在将文本从其它来源粘贴到 ActionScript 中时,请确保使用正确的字符。
如下表所示,可以使用反斜杠转义符 () 在字符串文本中定义其它字符:
转义序列
字符
b
退格符
f
换页符
n
换行符
r
回车符
t
制表符
unnnn
Unicode 字符,字符代码由十六进制数字 nnnn 指定;例如,u263a 为笑脸字符。
xnn
ASCII 字符,字符代码由十六进制数字 nn 指定。
'
单引号
"
双引号

单个反斜杠字符
length 属性
每个字符串都有 length 属性,其值等于字符串中的字符数。
空字符串和 null 字符串的长度均为 0。
处理字符串中的字符
字符串中的每个字符在字符串中都有一个索引位置(整数)。第一个字符的索引位置为 0。
您可以使用 charAt() 方法和 charCodeAt() 方法检查字符串各个位置上的字符:
var str:String = "hello world!";
for (var:i = 0; i < str.length; i++)
{
Trace ( str.charAt (i), "-", str.charCodeAt (i) );
}
此外,您还可以通过字符代码,使用 fromCharCode() 方法定义字符串,如下例所示:
var myStr:String = String.fromCharCode (104,101,108,108,111,32,119,111,114,108,100,33);
// 将 myStr 设置为"hello world!"
比较字符串
可以使用以下运算符比较字符串:<、<=、!=、==、=> 和 >。
在将这些运算符用于字符串时,ActionScript 会使用字符串中每个字符的字符代码值从左到右比较各个字符。
使用 == 和 != 运算符可比较两个字符串,也可以将字符串与其它类型的对象进行比较,如下例所示:

var str1:String = "1";
var total:uint = 1;
trace(str1 == total); // true
获取其它对象的字符串表示形式
可以获取任何类型对象的字符串表示形式。所有对象都提供了 toString() 方法来实现此目的:
在使用 + 连接运算符连接 String 对象和不属于字符串的对象时,无需使用 toString() 方法。对于给定对象,String() 全局函数返回的值与调用该对象的 toString() 方法返回的值相同。
连接字符串
字符串连接的含义是:将两个字符串按顺序合并为一个字符串。
可以使用 + 运算符来连接两个字符串,还可以使用 += 运算符来得到相同的结果,此外,String 类还包括 concat() 方法,可按如下方式对其进行使用:
var str1:String = "Bonjour";
var str2:String = "from";
var str3:String = "Paris";
var str4:String = str1.concat(" ", str2, " ", str3); // str4 == "Bonjour from Paris"
如果使用 + 运算符(或 += 运算符)对 String 对象和"非"字符串的对象进行运算,ActionScript 会自动将非字符串对象转换为 String 对象以计算该表达式。
但是,可以使用括号进行分组,为 + 运算符提供运算的上下文,如下例所示:
trace("Total: $" + (4.55 + 1.45)); // 输出:Total: $6
在字符串中查找子字符串和模式
子字符串是字符串内的字符序列。
模式是在 ActionScript 中通过字符串或正则表达式定义的。
通过字符位置查找子字符串
substr() 和 substring() 方法非常类似。两个方法都返回字符串的一个子字符串。并且两个方法都具有两个参数。在这两个方法中,第一个参数是给定字符串中起始字符的位置。不过,在 substr() 方法中,第二个参数是要返回的子字符串的"长度",而在 substring() 方法中,第二个参数是子字符串的"结尾"处字符的位置(该字符未包含在返回的字符串中)。
slice() 方法的功能类似于 substring() 方法。当指定两个非负整数作为参数时,其运行方式将完全一样。但是,slice() 方法可以使用负整数作为参数,此时字符位置将从字符串末尾开始向前算起。可以结合使用非负整数和负整数作为 slice() 方法的参数。
查找匹配子字符串的字符位置
可以使用 indexOf() 和 lastIndexOf() 方法在字符串内查找匹配的子字符串。
indexOf() 方法区分大小写。可以指定第二个参数以指出在字符串中开始进行搜索的起始索引位置。
lastIndexOf() 方法在字符串中查找子字符串的最后一个匹配项。如果为 lastIndexOf() 方法提供了第二个参数,搜索将从字符串中的该索引位置反向(从右到左)进行。
创建由分隔符分隔的子字符串数组
可使用 split() 方法创建子字符串数组,该数组根据分隔符进行划分。
split() 方法的第二个参数是可选参数,该参数定义所返回数组的最大大小。此外,还可以使用正则表达式作为分隔符。
在字符串中查找模式并替换子字符串
String 类提供了在字符串中处理模式的以下方法:
使用 match() 和 search() 方法可查找与模式相匹配的子字符串。
使用 replace() 方法可查找与模式相匹配的子字符串并使用指定子字符串替换它们。
查找匹配的子字符串
search() 方法返回与给定模式相匹配的第一个子字符串的索引位置,您还可以使用正则表达式定义要匹配的模式。search() 方法仅查找一个匹配项并返回其起始索引位置,即便在正则表达式中设置了 g(全局)标志。
match() 方法的工作方式与此类似。它搜索一个匹配的子字符串。但是,如果在正则表达式模式中使用了全局标志,match() 将返回一个包含匹配子字符串的数组。
替换匹配的子字符串
您可以使用 replace() 方法在字符串中搜索指定模式并使用指定的替换字符串替换匹配项。
在正则表达式中设置了 i (ignoreCase) 标志,则匹配的字符串是不区分大小写的;设置了 g (global) 标志,则会替换多个匹配项。
可以在替换字符串中包括以下 $ 替换代码。下表中显示的替换文本将被插入并替换 $ 替换代码:
$ 代码
替换文本
$$
$
$&
匹配的子字符串。
$`
字符串中位于匹配的子字符串前面的部分。该代码使用左单直引号字符 (`) 而不是单直引号 (') 或左单弯引号 (')。
$'
字符串中位于匹配的子字符串后的部分。该代码使用单直引号 (')。
$n
第 n 个捕获的括号组匹配项,其中 n 是 1-9 之间的数字,并且 $n 后面没有十进制数字。
$nn
第 nn 个捕获的括号组匹配项,其中 nn 是一个十进制的两位数 (01-99)。如果未定义第 nn 个捕获内容,则替换文本为空字符串。
也可以使用函数作为 replace() 方法的第二个参数。
在大小写之间转换字符串
如下例所示,toLowerCase() 方法和 toUpperCase() 方法分别将字符串中的英文字母字符转换为小写和大写。执行完这些方法后,源字符串仍保持不变。
这些方法可处理扩展字符,而并不仅限于 a-z 和 A-Z。
处理数组
使用数组可以在单数据结构中存储多个值。可以使用简单的索引数组(使用固定有序整数索引存储值),也可以使用复杂的关联数组(使用任意键存储值)。数组也可以是多维的,即包含本身是数组的元素。
数组基础知识
处理数组简介
数组是一种编程元素,它用作一组项目的容器。通常,数组中的所有项目都是相同类的实例,但这在 ActionScript 中并不是必需的。数组中的各个项目称为数组的"元素"。可以将数组视为变量的"文件柜"。可以将变量作为元素添加到数组中,也可以将数组作为单个变量使用;将这些变量作为组使用;或者,可以分别访问单个变量。
最常见的 ActionScript 数组类型是"索引数组",此数组将每个项目存储在编号位置(称为"索引"),您可以使用该编号来访问项目,如地址。Array 类用于表示索引数组。索引数组可以很好地满足大多数编程需要。索引数组的一个特殊用途是多维数组,此索引数组的元素也是索引数组(这些数组又包含其它元素)。另一种数组类型是"关联数组",该数组使用字符串"键"来标识各个元素,而不是使用数字索引。最后,对于高级用户,ActionScript 3.0 还包括 Dictionary 类(表示"字典"),在此数组中,您可以将任何类型的对象用作键来区分元素。
重要概念和术语
以下参考列表包含将会在本章中遇到的重要术语:
数组 (Array):用作容器以将多个对象组合在一起的对象。
关联数组 (Associative array):使用字符串键来标识各个元素的数组。
字典 (Dictionary):其项目由一对对象(称为键和值)组成的数组。它使用键来标识单个元素,而不是使用数字索引。
元素 (Element):数组中的单个项目。
索引 (Index):用于标识索引数组中的单个元素的数字"地址"。
索引数组 (Indexed array):这是一种标准类型的数组,它将每个元素存储在编号元素中,并使用数字(索引)来标识各个元素。
键 (Key):用于标识关联数组或字典中的单个元素的字符串或对象。
多维数组 (Multidimensional array):此数组包含的项目是数组,而不是单个值。
索引数组
索引数组存储一系列经过组织的单个或多个值,其中的每个值都可以通过使用一个无符号整数值进行访问。第一个索引始终是数字 0,且添加到数组中的每个后续元素的索引以 1 为增量递增。可以调用 Array 类构造函数或使用数组文本初始化数组来创建索引数组。
Array 类中还包含可用来修改索引数组的属性和方法。这些属性和方法几乎是专用于索引数组而非关联数组的。
索引数组使用无符号 32 位整数作为索引号。索引数组的最大大小为 232-1,即 4,294,967,295。如果要创建的数组大小超过最大值,则会出现运行时错误。
数组元素的值可以为任意数据类型。ActionScript 3.0 不支持"指定类型的数组"概念,也就是说,不能指定数组的所有元素都属于特定数据类型。
创建数组
Array 构造函数的使用有三种方式。
第一种,如果调用不带参数的构造函数,会得到空数组。
第二种,如果将一个数字用作 Array 构造函数的唯一参数,则会创建长度等于此数值的数组,并且每个元素的值都设置为 undefined。参数必须为介于值 0 和 4,294,967,295 之间的无符号整数。
第三种,如果调用构造函数并传递一个元素列表作为参数,将创建具有与每个参数对应的元素的数组。
也可以创建具有数组文本或对象文本的数组。
插入数组元素
可以使用 Array 类的三种方法(push()、unshift() 和 splice())将元素插入数组。push() 方法用于在数组末尾添加一个或多个元素。换言之,使用 push() 方法在数组中插入的最后一个元素将具有最大索引号。unshift() 方法用于在数组开头插入一个或多个元素,并且始终在索引号 0 处插入。splice() 方法用于在数组中的指定索引处插入任意数目的项目。
push() 和 unshift() 方法均返回一个无符号整数,它们表示修改后的数组长度。在用于插入元素时,splice() 方法返回空数组,这看上去也许有点奇怪,但考虑到 splice() 方法的多用途性,您便会觉得它更有意义。通过使用 splice() 方法,不仅可以将元素插入到数组中,而且还可以从数组中删除元素。用于删除元素时,splice() 方法将返回包含被删除元素的数组。
删除数组元素
可以使用 Array 类的三种方法(pop()、shift() 和 splice())从数组中删除元素。pop() 方法用于从数组末尾删除一个元素。换言之,它将删除位于最大索引号处的元素。shift() 方法用于从数组开头删除一个元素,也就是说,它始终删除索引号 0 处的元素。splice() 方法既可用来插入元素,也可以删除任意数目的元素,其操作的起始位置位于由发送到此方法的第一个参数指定的索引号处。
pop() 和 shift() 方法均返回已删除的项。由于数组可以包含任意数据类型的值,因而返回值的数据类型为 Object。splice() 方法将返回包含被删除值的数组。
在数组元素上使用 delete 运算符。delete 运算符用于将数组元素的值设置为 undefined,但它不会从数组中删除元素。
可以使用数组的 length 属性截断数组。如果将数组的 length 属性设置为小于数组当前长度的值,则会截断数组,在索引号高于 length 的新值减 1 处所存储的任何元素将被删除。
对数组排序
可以使用三种方法(reverse()、sort() 和 sortOn())通过排序或反向排序来更改数组的顺序。所有这些方法都用来修改现有数组。reverse() 方法用于按照以下方式更改数组的顺序:最后一个元素变为第一个元素,倒数第二个元素变为第二个元素,依此类推。sort() 方法可用来按照多种预定义的方式对数组进行排序,甚至可用来创建自定义排序算法。sortOn() 方法可用来对对象的索引数组进行排序,这些对象具有一个或多个可用作排序键的公共属性。
reverse() 方法不带参数,也不返回值,但可以将数组从当前顺序切换为相反顺序。
sort() 方法按照"默认排序顺序"重新安排数组中的元素。默认排序顺序具有以下特征:
排序区分大小写,也就是说大写字符优先于小写字符。例如,字母 D 优先于字母 b。
排序按照升序进行,也就是说低位字符代码(例如 A)优先于高位字符代码(例如 B)。
排序将相同的值互邻放置,并且不区分顺序。
排序基于字符串,也就是说,在比较元素之前,先将其转换为字符串(例如,10 优先于 3,因为相对于字符串 "3" 而言,字符串 "1" 具有低位字符代码)。
sort() 方法具有 options 参数,可通过该参数改变默认排序顺序的各个特征。options 是由 Array 类中的一组静态常量定义的,如以下列表所示:
Array.CASEINSENSITIVE:此选项可使排序不区分大小写。例如,小写字母 b 优先于大写字母 D。
Array.DESCENDING:用于颠倒默认的升序排序。例如,字母 B 优先于字母 A。
Array.UNIQUESORT:如果发现两个相同的值,此选项将导致排序中止。
Array.NUMERIC:这会导致排序按照数字顺序进行,比方说 3 优先于 10。
您也可以编写自定义排序函数,然后将其作为参数传递给 sort() 方法。
sortOn() 方法是为具有包含对象的元素的索引数组设计的。这些对象应至少具有一个可用作排序键的公共属性。如果将 sortOn() 方法用于任何其它类型的数组,则会产生意外结果。
sortOn() 方法定义两个参数 fieldName 和 options。必须将 fieldName 参数指定为字符串。Array.NUMERIC 参数用于确保按照数字顺序进行排序,而不是按照字母顺序。
通常,sort() 和 sortOn() 方法用来修改数组。如果要对数组排序而又不修改现有数组,请将 Array.RETURNINDEXEDARRAY 常量作为 options 参数的一部分进行传递。此选项将指示方法返回反映排序的新数组同时保留原始数组原封不动。方法返回的数组为由反映新排序顺序的索引号组成的简单数组,不包含原始数组的任何元素。
查询数组
Array 类中的其余四种方法 concat()、join()、slice() 和 toString() 用于查询数组中的信息,而不修改数组。concat() 和 slice() 方法返回新数组;而 join() 和 toString() 方法返回字符串。concat() 方法将新数组和元素列表作为参数,并将其与现有数组结合起来创建新数组。slice() 方法具有两个名为 startIndex 和 endIndex 的参数,并返回一个新数组,它包含从现有数组分离出来的元素副本。分离从 startIndex 处的元素开始,到 endIndex 处的前一个元素结束。值得强调的是,endIndex 处的元素不包括在返回值中。
可以使用 join() 和 toString() 方法查询数组,并将其内容作为字符串返回。如果 join() 方法没有使用参数,则这两个方法的行为相同,它们都返回包含数组中所有元素的列表(以逗号分隔)的字符串。与 toString() 方法不同,join() 方法接受名为 delimiter 的参数;可以使用此参数,选择要用作返回字符串中各个元素之间分隔符的符号。
对于 join() 方法,应注意的一个问题是,无论为主数组元素指定的分隔符是什么,为嵌套数组返回的值始终以逗号作为分隔符。
关联数组
关联数组有时候也称为"哈希"或"映射",它使用"键"而非数字索引来组织存储的值。关联数组中的每个键都是用于访问一个存储值的唯一字符串。关联数组为 Object 类的实例,也就是说每个键都与一个属性名称对应。关联数组是键和值对的无序集合。在代码中,不应期望关联数组的键按特定的顺序排列。
ActionScript 3.0 中引入了名为"字典"的高级关联数组。字典是 flash.utils 包中 Dictionary 类的实例,使用的键可以为任意数据类型,但通常为 Object 类的实例。换言之,字典的键不局限于 String 类型的值。
具有字符串键的关联数组
在 ActionScript 3.0 中有两种创建关联数组的方法。
第一种方法是使用 Object 构造函数,它的优点是可以使用对象文本初始化数组。Object 类的实例(也称作"通用对象")在功能上等同于关联数组。通用对象的每个属性名称都用作键,提供对存储的值的访问。
如果在声明数组时不需要初始化,可以使用 Object 构造函数创建数组。
使用对象文本或 Object 类构造函数创建数组后,可以使用括号运算符 ([]) 或点运算符 (.) 在数组中添加值。
如名为 aspect ratio 的键包含空格字符。也就是说,空格字符可以与括号运算符一起使用,但试图与点运算符一起使用时会生成一个错误。不建议在键名称中使用空格。
第二种关联数组创建方法是使用 Array 构造函数,然后使用括号运算符 ([]) 或点运算符 (.) 将键和值对添加到数组中。如果将关联数组声明为 Array 类型,则将无法使用对象文本初始化该数组。
使用 Array 构造函数创建关联数组没有什么优势。即使使用 Array 构造函数或 Array 数据类型,也不能将 Array 类的 Array.length 属性或任何方法用于关联数组。最好将 Array 构造函数用于创建索引数组。
具有对象键的关联数组
可以使用 Dictionary 类创建使用对象而非字符串作为键的关联数组。这样的数组有时候也称作字典、哈希或映射。可以使用属性访问运算符 ([]) 访问与每个键关联的值。
使用对象键循环访问
可以使用 for..in 循环或 for each..in 循环来循环访问 Dictionary 对象的内容。for..in 循环用来基于键进行循环访问;而 for each..in 循环用来基于与每个键关联的值进行循环访问。
可以使用 for..in 循环直接访问 Dictionary 对象的对象键。还可以使用属性访问运算符 ([]) 访问 Dictionary 对象的值。
可以使用 for each..in 循环直接访问 Dictionary 对象的值。
多维数组
多维数组将其它数组作为其元素。在多维数组中,可以使用任意组合的索引数组和关联数组。
两个索引数组
使用两个索引数组时,可以将结果呈现为表或电子表格。第一个数组的元素表示表的行,第二个数组的元素表示表的列。可以使用括号记号访问任意任务列表中的单个项。
具有索引数组的关联数组
克隆数组
Array 类不具有复制数组的内置方法。可以通过调用不带参数的 concat() 或 slice() 方法来创建数组的"浅副本"。在浅副本中,如果原始数组具有对象元素,则仅复制指向对象的引用而非对象本身。与原始数组一样,副本也指向相同的对象。对对象所做的任何更改都会在两个数组中反映出来。
在"深副本"中,将复制原始数组中的所有对象,从而使新数组和原始数组指向不同的对象。深度复制需要多行代码,通常需要创建函数。可以将此类函数作为全局实用程序函数或 Array 子类的方法来进行创建。
处理事件
事件处理基础知识
事件处理简介
在 ActionScript 3.0 中,每个事件都由一个事件对象表示。事件对象是 Event 类或其某个子类的实例。事件对象不但存储有关特定事件的信息,还包含便于操作事件对象的方法。
当 Flash Player 检测到事件时,它会创建一个事件对象以表示该特定事件。创建事件对象之后,Flash Player 即"调度"该事件对象,这意味着将该事件对象传递给作为事件目标的对象。作为所调度事件对象的目标的对象称为"事件目标"。但是,如果事件目标在显示列表中,则在显示列表层次结构中将事件对象向下传递,直到到达事件目标为止。在某些情况下,该事件对象随后会沿着相同路线在显示列表层次结构中向上"冒泡"回去。显示列表层次结构中的这种遍历行为称为"事件流"。
您可以使用事件侦听器"侦听"代码中的事件对象。"事件侦听器"是您编写的用于响应特定事件的函数或方法。要确保您的程序响应事件,必须将事件侦听器添加到事件目标,或添加到作为事件对象事件流的一部分的任何显示列表对象。
无论何时编写事件侦听器代码,该代码都会采用以下基本结构(以粗体显示的元素是占位符,您将针对具体情况对其进行填写):
function eventResponse(eventObject:EventType):void
{
// 此处是为响应事件而执行的动作。
}

eventTarget.addEventListener(EventType.EVENT_NAME, eventResponse);
此代码执行两个操作。首先,它定义一个函数,这是指定为响应事件而执行的动作的方法。接下来,调用源对象的 addEventListener() 方法,实际上就是为指定事件"订阅"该函数,以便当该事件发生时,执行该函数的动作。当事件实际发生时,事件目标将检查其注册为事件侦听器的所有函数和方法的列表。然后,它依次调用每个对象,以将事件对象作为参数进行传递。
重要概念和术语
默认行为 (Default behavior):某些事件包含通常与事件一起发生的行为(称为默认行为)。
调度 (Dispatch):通知事件侦听器发生了事件。
事件 (Event):对象可以通知其它对象它所发生的情况。
事件流 (Event flow):如果显示列表中的对象(屏幕上显示的对象)发生事件,则会向包含该对象的所有对象通知此事件,并依次通知其事件侦听器。此过程从舞台开始,并在显示列表中一直进行到发生事件的实际对象,然后再返回到舞台。此过程称为事件流。
事件对象 (Event object):此对象包含发生的特定事件的相关信息,当调度事件时,此信息将被发送到所有侦听器。
事件目标 (Event target):实际调度事件的对象。
侦听器 (Listener):对象或在对象中注册其自身的函数,用于指示发生特定事件时应通知它。
ActionScript 3.0 中的事件处理
ActionScript 3.0 引入了单一事件处理模型,以替代以前各语言版本中存在的众多不同的事件处理机制。该新事件模型基于文档对象模型 (DOM) 第 3 级事件规范。虽然 SWF 文件格式并不专门遵循文档对象模型标准,但显示列表和 DOM 结构之间存在的相似性足以使 DOM 事件模型的实现成为可能。显示列表中的对象类似于 DOM 层次结构中的节点,在本讨论中,术语"显示列表对象"和"节点"可互换使用。
Flash Player 实现的 DOM 事件模型包括一个名为"默认行为"的概念。"默认行为"是 Flash Player 作为特定事件的正常后果而执行的操作。
默认行为
开发人员通常负责编写响应事件的代码。但在某些情况下,行为通常与某一事件关联,使得 Flash Player 会自动执行该行为,除非开发人员添加了取消该行为的代码。由于 Flash Player 会自动表现该行为,因此这类行为称为默认行为。
并非所有默认行为都可以阻止。
许多类型的事件对象没有关联的默认行为。
事件流
只要发生事件,Flash Player 就会调度事件对象。如果事件目标不在显示列表中,则 Flash Player 将事件对象直接调度到事件目标。例如,Flash Player 将 progress 事件对象直接调度到 URLStream 对象。但是,如果事件目标在显示列表中,则 Flash Player 将事件对象调度到显示列表,事件对象将在显示列表中穿行,直到到达事件目标。
"事件流"说明事件对象如何在显示列表中穿行。显示列表以一种可以描述为树的层次结构形式进行组织。位于显示列表层次结构顶部的是舞台,它是一种特殊的显示对象容器,用作显示列表的根。舞台由 flash.display.Stage 类表示,且只能通过显示对象访问。每个显示对象都有一个名为 stage 的属性,该属性表示应用程序的舞台。
当 Flash Player 调度事件对象时,该事件对象进行一次从舞台到"目标节点"的往返行程。DOM 事件规范将目标节点定义为代表事件目标的节点。也就是说,目标节点是发生了事件的显示列表对象。例如,如果用户单击名为 child1 的显示列表对象,Flash Player 将使用 child1 作为目标节点来调度事件对象。
从概念上来说,事件流分为三部分。第一部分称为捕获阶段,该阶段包括从舞台到目标节点的父节点范围内的所有节点。第二部分称为目标阶段,该阶段仅包括目标节点。第三部分称为冒泡阶段。冒泡阶段包括从目标节点的父节点返回到舞台的行程中遇到的节点。
如果您将显示列表想像为一个垂直的层次结构,其中舞台位于顶层(如下图显示),那么这些阶段的名称就更容易理解了:

如果用户单击 Child1,Flash Player 将向事件流调度一个事件对象。如下面的图像所示,对象的行程从 Stage 开始,向下移动到 Parent,然后移动到 Child1,再"冒泡"返回到 Stage:在行程中重新经过 Parent,再返回到 Stage。

在该示例中,捕获阶段在首次向下行程中包括 Stage 和 Parent。目标阶段包括在 Child1 花费的时间。冒泡阶段包括在向上返回到根节点的行程中遇到的 Parent 和 Stage。
事件流使现在的事件处理系统比 ActionScript 程序员以前使用的事件处理系统功能更为强大。早期版本的 ActionScript 中没有事件流,这意味着事件侦听器只能添加到生成事件的对象。在 ActionScript 3.0 中,您不但可以将事件侦听器添加到目标节点,还可以将它们添加到事件流中的任何节点。
当用户界面组件包含多个对象时,沿事件流添加事件侦听器的功能十分有用。例如,按钮对象通常包含一个用作按钮标签的文本对象。如果无法将侦听器添加到事件流,您将必须将侦听器添加到按钮对象和文本对象,以确保您收到有关在按钮上任何位置发生的单击事件的通知。而事件流的存在则使您可以将一个事件侦听器放在按钮对象上,以处理文本对象上发生的单击事件或按钮对象上未被文本对象遮住的区域上发生的单击事件。
不过,并非每个事件对象都参与事件流的所有三个阶段。某些类型的事件(例如 enterFrame 和 init 类型的事件)会直接调度到目标节点,并不参与捕获阶段和冒泡阶段。其它事件可能以不在显示列表中的对象为目标,例如调度到 Socket 类的实例的事件。这些事件对象也将直接流至目标对象,而不参与捕获和冒泡阶段。
事件对象
在新的事件处理系统中,事件对象有两个主要用途。首先,事件对象通过将特定事件的信息存储在一组属性中,来代表实际事件。第二,事件对象包含一组方法,可用于操作事件对象和影响事件处理系统的行为。
为方便对这些属性和方法的访问,Flash Player API 定义了一个 Event 类,作为所有事件对象的基类。Event 类定义一组基本的适用于所有事件对象的属性和方法。
了解 Event 类属性
Event 类定义许多只读属性和常数,以提供有关事件对象的重要信息。以下内容尤其重要:
事件对象类型由常数表示,并存储在 Event.type 属性中。
事件的默认行为是否可以被阻止由布尔值表示,并存储在 Event.cancelable 属性中。
事件流信息包含在其余属性中。
事件对象类型
每个事件对象都有关联的事件类型。数据类型以字符串值的形式存储在 Event.type 属性中。知道事件对象的类型是非常有用的,这样您的代码就可以区分不同类型的事件。
大约有 20 多种事件类型与 Event 类自身关联并由 Event 类常数表示,其中某些数据类型显示在摘自 Event 类定义的以下代码中:
package flash.events
{
public class Event
{
// 类常数
public static const ACTIVATE:String = "activate";
public static const ADDED:String = "added";
// 为简便起见,省略了其余常数
}
}
这些常数提供了引用特定事件类型的简便方法。您应使用这些常数而不是它们所代表的字符串。如果您的代码中拼错了某个常数名称,编译器将捕获到该错误,但如果您改为使用字符串,则编译时可能不会出现拼写错误,这可能导致难以调试的意外行为。
默认行为信息
代码可通过访问 cancelable 属性来检查是否可以阻止任何指定事件对象的默认行为。cancelable 属性包含一个布尔值,用于指示是否可以阻止默认行为。您可以使用 preventDefault() 方法阻止或取消与少量事件关联的默认行为。
事件流信息
其余 Event 类属性包含有关事件对象及其与事件流的关系的重要信息,如以下列表所述:
bubbles 属性包含有关事件流中事件对象参与的部分的信息。
eventPhase 属性指示事件流中的当前阶段。
target 属性存储对事件目标的引用。
currentTarget 属性存储对当前正在处理事件对象的显示列表对象的引用。
bubbles 属性
如果事件对象参与事件流的冒泡阶段,则将该事件称为"冒泡",这指的是从目标节点将事件对象往回传递,经过目标节点的父节点,直到到达舞台。Event.bubbles 属性存储一个布尔值,用于指示事件对象是否参与冒泡阶段。由于冒泡的所有事件还参与捕获和目标阶段,因此这些事件参与事件流的所有三个阶段。如果值为 true,则事件对象参与所有三个阶段。如果值为 false,则事件对象不参与冒泡阶段。
eventPhase 属性
您可以通过调查任何事件对象的 eventPhase 属性来确定事件阶段。eventPhase 属性包含一个无符号整数值,该值代表三个事件流阶段中的一个阶段。Flash Player API 定义了单独的 EventPhase 类,该类包含三个对应于三个无符号整数值的常量,如以下摘录代码中所示:
package flash.events
{
public final class EventPhase
{
public static const CAPTURING_PHASE:uint = 1;
public static const AT_TARGET:uint = 2;
public static const BUBBLING_PHASE:uint = 3;
}
}
这些常数对应于 eventPhase 属性的三个有效值。使用这些常数可以使您的代码可读性更好。例如,如果要确保仅当事件目标在目标阶段中时才调用名为 myFunc() 的函数,您可以使用以下代码来测试此条件:
if (event.eventPhase == EventPhase.AT_TARGET)
{
myFunc();
}
target 属性
target 属性包含对作为事件目标的对象的引用。在某些情况下,这很简单,例如当麦克风变为活动状态时,事件对象的目标是 Microphone 对象。但是,如果目标在显示列表中,则必须考虑显示列表层次结构。例如,如果用户在包括重叠的显示列表对象的某一点输入一个鼠标单击,则 Flash Player 始终会选择距离舞台层次最深的对象作为事件目标。
对于复杂的 SWF 文件,特别是那些通常使用更小的子对象来修饰按钮的 SWF 文件,target 属性可能并不常用,因为它通常指向按钮的子对象,而不是按钮。在这些情况下,常见的做法是将事件侦听器添加到按钮并使用 currentTarget 属性,因为该属性指向按钮,而 target 属性可能指向按钮的子对象。
currentTarget 属性
currentTarget 属性包含对当前正在处理事件对象的对象的引用。
了解 Event 类方法
共有三种类别的 Event 类方法:
实用程序方法:可以创建事件对象的副本或将其转换为字符串
事件流方法:用于从事件流中删除事件对象
默认行为方法:可阻止默认行为或检查是否已阻止默认行为
Event 流实用程序方法
Event 类中有两个实用程序方法。clone() 方法用于创建事件对象的副本。toString() 方法用于生成事件对象属性的字符串表示形式以及它们的值。这两个方法都由事件模型系统在内部使用,但对开发人员公开以用于一般用途。
对于创建 Event 类的子类的高级开发人员来说,必须覆盖和实现两个实用程序方法的版本,以确保事件子类正常使用。
停止事件流
可以调用 Event.stopPropogation() 方法或 Event.stopImmediatePropogation() 方法来阻止在事件流中继续执行事件对象。这两种方法几乎相同,只有在是否允许执行当前节点的其它事件侦听器方面不同:
Event.stopPropogation() 方法可阻止事件对象移动到下一个节点,但只有在允许执行当前节点上的任何其它事件侦听器之后才起作用。
Event.stopImmediatePropogation() 方法也阻止事件对象移动到下一个节点,但不允许执行当前节点上的任何其它事件侦听器。
调用其中任何一个方法对是否发生与事件关联的默认行为没有影响。
取消默认事件行为
与取消默认行为有关的两个方法是 preventDefault() 方法和 isDefaultPrevented() 方法。调用 preventDefault() 方法可取消与事件关联的默认行为。要查看是否已针对事件对象调用了 preventDefault(),请调用 isDefaultPrevented() 方法,如果已经调用,该方法将返回值 true,否则返回值 false。
仅当可以取消事件的默认行为时,preventDefault() 方法才起作用。可通过参考该事件类型的 API 文档或使用 ActionScript 检查事件对象的 cancelable 属性来确定是否属于这种情况。
取消默认行为对事件对象通过事件流的进度没有影响。使用 Event 类的事件流方法可以从事件流中删除事件对象。
事件侦听器
事件侦听器也称为事件处理函数,是 Flash Player 为响应特定事件而执行的函数。添加事件侦听器的过程分为两步。首先,为 Flash Player 创建一个为响应事件而执行的函数或类方法。这有时称为侦听器函数或事件处理函数。然后,使用 addEventListener() 方法,在事件的目标或位于适当事件流上的任何显示列表对象中注册侦听器函数。
创建侦听器函数
创建侦听器函数是 ActionScript 3.0 事件模型与 DOM 事件模型不同的一个方面。在 DOM 事件模型中,事件侦听器和侦听器函数之间有一个明显的不同:即事件侦听器是实现 EventListener 接口的类的实例,而侦听器是该类的名为 handleEvent() 的方法。在 DOM 事件模型中,您注册的是包含侦听器函数的类实例,而不是实际的侦听器函数。
在 ActionScript 3.0 事件模型中,事件侦听器和侦听器函数之间没有区别。ActionScript 3.0 没有 EventListener 接口,侦听器函数可以在类外部定义,也可以定义为类的一部分。此外,无需将侦听器函数命名为 handleEvent() ─ 可以将它们命名为任何有效的标识符。在 ActionScript 3.0 中,您注册的是实际侦听器函数的名称。
管理事件侦听器
使用 IEventDispatcher 接口的方法来管理侦听器函数。IEventDispatcher 接口是 ActionScript 3.0 版本的 DOM 事件模型的 EventTarget 接口。虽然名称 IEventDispatcher 似乎暗示着其主要用途是发送(调度)事件对象,但该类的方法实际上更多用于注册、检查和删除事件侦听器。IEventDispatcher 接口定义五个方法,如以下代码中所示:
package flash.events
{
public interface IEventDispatcher
{
function addEventListener(eventName:String,
listener:Object,
useCapture:Boolean=false,
priority:Integer=0,
useWeakReference:Boolean=false):Boolean;

function removeEventListener(eventName:String,
listener:Object,
useCapture:Boolean=false):Boolean;

function dispatchEvent(eventObject:Event):Boolean;

function hasEventListener(eventName:String):Boolean;
function willTrigger(eventName:String):Boolean;
}
}
Flash Player API 使用 EventDispatcher 类来实现 IEventDispatcher 接口,该类用作可以是事件目标或事件流一部分的所有类的基类。例如,DisplayObject 类继承自 EventDispatcher 类。这意味着,显示列表中的所有对象都可以访问 IEventDispatcher 接口的方法。
添加事件侦听器
addEventListener() 方法是 IEventDispatcher 接口的主要函数。使用它来注册侦听器函数。两个必需的参数是 type 和 listener。type 参数用于指定事件的类型。listener 参数用于指定发生事件时将执行的侦听器函数。listener 参数可以是对函数或类方法的引用。
指定 listener 参数时,不要使用括号。
通过使用 addEventListener() 方法的 useCapture 参数,可以控制侦听器将处于活动状态的事件流阶段。如果 useCapture 设置为 true,侦听器将在事件流的捕获阶段成为活动状态。如果 useCapture 设置为 false,侦听器将在事件流的目标阶段和冒泡阶段处于活动状态。要在事件流的所有阶段侦听某一事件,您必须调用 addEventListener() 两次,第一次调用时将 useCapture 设置为 true,第二次调用时将 useCapture 设置为 false。
addEventListener() 方法的 priority 参数并不是 DOM Level 3 事件模型的正式部分。ActionScript 3.0 中包括它是为了在组织事件侦听器时提供更大的灵活性。调用 addEventListener() 时,可以将一个整数值作为 priority 参数传递,以设置该事件侦听器的优先级。默认值为 0,但您可以将它设置为负整数值或正整数值。将优先执行此数字较大的事件侦听器。对于具有相同优先级的事件侦听器,则按它们的添加顺序执行,因此将优先执行较早添加的侦听器。
可以使用 useWeakReference 参数来指定对侦听器函数的引用是弱引用还是正常引用。通过将此参数设置为 true,可避免侦听器函数在不再需要时仍然存在于内存中的情况。Flash Player 使用一项称为"垃圾回收"的技术从内存中清除不再使用的对象。如果不存在对某个对象的引用,则该对象被视为不再使用。垃圾回收器不考虑弱引用,这意味着如果侦听器函数仅具有指向它的弱引用,则符合垃圾回收条件。
该参数的一个重要后果与显示对象事件的处理有关。通常,您可能希望从显示列表中删除显示对象时,也将其从内存中删除。但是,如果其它对象已在 useWeakReference 参数设置为 false(默认值)时作为侦听器订阅该显示对象,该显示对象将继续存在于 Flash Player 的内存中,即使它已不再显示在屏幕中。要解决该问题,可以使所有侦听器在 useWeakReference 参数设置为 true 时订阅该显示对象,或者使用 removeEventListener() 方法从该显示对象中删除所有事件侦听器。
删除事件侦听器
可以使用 removeEventListener() 方法删除不再需要的事件侦听器。删除将不再使用的所有侦听器是个好办法。必需的参数包括 eventName 和 listener 参数,这与 addEventListener() 方法所需的参数相同。回想一下,您可以通过调用 addEventListener() 两次(第一次调用时将 useCapture 设置为 true,第二次调用时将其设置为 false),在所有事件阶段侦听事件。要删除这两个事件侦听器,您需要调用 removeEventListener() 两次,第一次调用时将 useCapture 设置为 true,第二次调用时将其设置为 false。
调度事件
高级程序员可以使用 dispatchEvent() 方法将自定义事件对象调度到事件流。该方法唯一接受的参数是对事件对象的引用,此事件对象必须是 Event 类的实例或子类。调度后,事件对象的 target 属性将设置为对其调用了 dispatchEvent() 的对象。
检查现有的事件侦听器
IEventDispatcher 接口的最后两个方法提供有关是否存在事件侦听器的有用信息。如果在特定显示列表对象上发现特定事件类型的事件侦听器,hasEventListener() 方法将返回 true。如果发现特定显示列表对象的侦听器,willTrigger() 方法也会返回 true。但 willTrigger() 不但检查该显示对象上的侦听器,还会检查该显示列表对象在事件流所有阶段中的所有父级上的侦听器。
没有侦听器的错误事件
ActionScript 3.0 中处理错误的主要机制是异常而不是事件,但对于异步操作(例如加载文件),异常处理不起作用。如果在这样的异步操作中发生错误,Flash Player 会调度一个错误事件对象。如果不为错误事件创建侦听器,Flash Player 的调试器版本将打开一个对话框,其中包含有关该错误的信息。
大多数错误事件基于 ErrorEvent 类,而且同样具有一个名为 text 的属性,它用于存储 Flash Player 显示的错误消息。两个异常是 StatusEvent 和 NetStatusEvent 类。这两个类都具有一个 level 属性(StatusEvent.level 和 NetStatusEvent.info.level)。当 level 属性的值为"error"时,这些事件类型被视为错误事件。
错误事件将不会导致 SWF 文件停止运行。它仅在浏览器插件和独立播放器的调试器版本上显示为对话框,在创作播放器的输出面板中显示为消息,在 Adobe Flex Builder 2 的日志文件中显示为条目,而在 Flash Player 的发行版中则根本不显示。
使用正则表达式
正则表达式描述用于查找和处理字符串中的匹配文本的模式。切记,不同的编程环境实现正则表达式的方式也不同。ActionScript 3.0 按照 ECMAScript 第 3 版语言规范 (ECMA-262) 中的定义实现正则表达式。
正则表达式基础知识
使用正则表达式简介
正则表达式描述字符模式。通常,正则表达式用于验证文本值是否符合特定模式,或者替换与特定模式匹配的部分文本值。
正则表达式文本是使用正斜杠 (/) 界定的。
重要概念和术语
转义字符 (Escape character):此字符指示应将后面的字符视为元字符,而不是字面字符。在正则表达式语法中,反斜杠字符 () 就是转义字符,因此反斜杠后跟另一个字符是一个特殊代码,而不仅仅是字符本身。
标志 (Flag):此字符指定有关应如何使用正则表达式模式的一些选项,如是否区分大写和小写字符。
元字符 (Metacharacter):在正则表达式模式中具有特殊含义的字符,它与从字面意义上在模式中表示该字符相对。
数量表示符 (Quantifier):一个或几个字符,指示应将模式部分重复多少次。
正则表达式 (Regular expression):用于定义字符模式的程序语句,它可用来确认其它字符串是否与该模式匹配,或者替换字符串的一部分。
正则表达式语法
建正则表达式实例
有两种方法可以创建正则表达式实例。一种方法是使用正斜杠字符 (/) 来界定正则表达式,另一种是使用 new 构造函数。
正斜杠界定正则表达式的方式与用引号界定字符串文本的方式相同。正斜杠内的正则表达式部分定义"模式"。正则表达式还可以在后一个界定斜杠后包含"标志"。这些标志也看作是正则表达式的一部分,但是它们独立于模式。
使用 new 构造函数时,使用两个字符串来定义正则表达式。第一个字符串定义模式,第二个字符串定义标志。
如果在使用正斜杠界定符定义的正则表达式中包含正斜杠,则必须在正斜杠前面加上反斜杠 () 转义字符。
要在使用 new 构造函数定义的正则表达式中包含引号,您必须在引号前面加上反斜杠 () 转义字符(就像定义任何 String 文本一样)。请勿在使用正斜杠界定符定义的正则表达式中对引号使用反斜杠转义字符。同样地,不要在使用 new 构造函数定义的正则表达式中对正斜杠使用转义字符。
此外,在使用 new 构造函数定义的正则表达式中,要使用以反斜杠 () 字符开头的元序列,请键入两个反斜杠字符。
字符、元字符和元序列
关于元字符
下表总结了可以在正则表达式中使用的元字符:
元字符
描述
^(尖号)
匹配字符串的开头。设置 m (multiline) 标志后,尖号还匹配行的开头。请注意,尖号用在字符类的开头时表示符号反转而非字符串的开头。
$(美元符号)
匹配字符串的结尾。设置 m (multiline) 标志后,$ 还匹配换行 (n) 字符前面的位置。
(反斜杠)
对特殊字符的特殊元字符含义进行转义。
此外,如果要在正则表达式文本中使用正斜杠字符,也要使用反斜杠字符。
.(点)
匹配任意单个字符。 只有设置 s (dotall) 标志时,点才匹配换行字符 (n)。
*(星号)
匹配前面重复零次或多次的项目。
+(加号)
匹配前面重复一次或多次的项目。
?(问号)
匹配前面重复零次或一次的项目。
( 和 )
在正则表达式中定义组。以下情况下使用组:
限制逻辑"或"字符 | 的范围:/(a|b|c)d/
定义数量表示符的范围:/(walla.){1,2}/
用在逆向引用中。例如,下面的正则表达式中的 1 匹配模式的第一个括号组中的匹配内容:
/(w*) is repeated: 1/
[ 和 ]
定义字符类,字符类定义单个字符可能的匹配:
/[aeiou]/ 匹配所指定字符中的任意一个。
在字符类中,使用连字符 (-) 指定字符的范围:
/[A-Z0-9]/ 匹配从 A 到 Z 的大写字母或 0 到 9 的数字。
在字符类中,插入反斜杠对 ] 和- 字符进行转义:
/[+-]d+/ 匹配一个或多个数字前面的 + 或 -。
在字符类中,以下字符(通常为元字符)被看作一般字符(非元字符),不需要反斜杠:
/[$£]/ 匹配 $ 或 £。。
|(竖线)
用于逻辑"或"操作,匹配左侧或右侧的部分:
/abc|xyz/ 匹配 abc 或 xyz。










关于元序列
元序列是在正则表达式模式中具有特殊含义的字符序列。下表说明了这些元序列:
元序列
描述
{n}{n,}和
{n,n}
指定前一项目的数值数量或数量范围:
/A{27}/ 匹配重复 27 次的字符 A。
/A{3,}/ 匹配重复 3 次或更多次的字符 A。
/A{3,5}/ 匹配重复 3 到 5 次的字符 A。
b
匹配单词字符和非单词字符之间的位置。如果字符串中的第一个或最后一个字符是单词字符,则也匹配字符串的开头或结尾。
B
匹配两个单词字符之间的位置。也匹配两个非单词字符之间的位置。
d
匹配十进制数字。
D
匹配除数字以外的任何字符。
f
匹配换页符。
n
匹配换行符。
r
匹配回车符。
s
匹配任何空白字符(空格、制表符、换行符或回车符)。
S
匹配除空白字符以外的任何字符。
t
匹配制表符。
unnnn
匹配字符代码由十六进制数字 nnnn 指定的 Unicode 字符。例如,u263a 是一个笑脸字符。
v
匹配垂直换页符。
w
匹配单词字符(A-Z、a-z、0-9 或 _)。请注意,w 不匹配非英文字符,如 e、O 或 A。
W
匹配除单词字符以外的任何字符。
xnn
匹配具有指定 ASCII 值(由十六进制数字 nn 定义)的字符。
字符类
可以使用字符类指定字符列表以匹配正则表达式中的一个位置。使用方括号([ 和 ])定义字符类。
字符类中的转义序列
通常在正则表达式中具有特殊含义的大多数元字符和元序列在字符类中"不具有"那些特殊含义。
下表中列出的三个字符功能与元字符相同,在字符类中具有特殊含义:
元字符
在字符类中的含义
]
定义字符类的结尾。
-
定义字符范围(请参阅字符类中字符的范围)。

定义元序列并撤销元字符的特殊含义。
对于要识别为字面字符(无特殊元字符含义)的任何字符,必须在该字符前面加反斜杠转义字符。
除能够保持特殊含义的元字符外,下列元序列在字符类中也具有元序列功能:
元序列
在字符类中的含义
n
匹配换行符。
r
匹配回车符。
t
匹配制表符。
unnnn
匹配具有指定 Unicode 代码点值(由十六进制数字 nnnn 定义)的字符。
xnn
匹配具有指定 ASCII 值(由十六进制数字 nn 定义)的字符。
其它正则表达式元序列和元字符在字符类中看作普通字符。
字符类中字符的范围
使用连字符指定字符的范围,例如 A-Z、a-z 或 0-9。这些字符必须在字符类中构成有效的范围。您还可以使用 xnn ASCII 字符代码通过 ASCII 值指定范围。
反转的字符类
如果在字符类的开头使用尖号 (^) 字符,则将反转该集合的意义,即未列出的任何字符都认为匹配。必须在字符类的"开头"键入尖号 (^) 字符以表示反转。否则,您只是将尖号字符添加到字符类的字符中。


数量表示符
使用数量表示符指定字符或序列在模式中的重复次数,如下所示:
数量表示符元字符
描述
*(星号)
匹配前面重复零次或多次的项目。
+(加号)
匹配前面重复一次或多次的项目。
?(问号)
匹配前面重复零次或一次的项目。
{n}
{n,}

{n,n}
指定前一项目的数值数量或数量范围:
/A{27}/ 匹配重复 27 次的字符 A。
/A{3,}/ 匹配重复 3 次或更多次的字符 A。
/A{3,5}/ 匹配重复 3 到 5 次的字符 A。
您可以将数量表示符应用到单个字符、字符类或组。
您可以在应用数量表示符的括号组内使用数量表示符。
默认情况下,正则表达式执行所谓"无限匹配"。正则表达式中的任何子模式(如 .*)都会尝试在字符串中匹配尽可能多的字符,然后再执行正则表达式的下一部分。在所有数量表示符后添加问号 (?) 以将其更改为所谓"惰性数量表示符"。使用惰性数量表示符 ? 匹配后跟数量最少(惰性)的字符。
有关数量表示符,请牢记以下几点:
数量表示符 {0} 和 {0,0} 不会从匹配中排除项目。
不要结合使用多个数量表示符,例如 /abc+*/ 中。
除非设置 s (dotall) 标志,否则点 (.) 不能跨越多行,即使后跟 * 数量表示符。
逻辑"或"
在正则表达式中使用 |(竖线)字符可使正则表达式引擎考虑其它匹配。
您可以使用括号定义组以限制逻辑"或"字符 | 的范围。

您可以使用括号在正则表达式中指定组,组是模式的子部分。您可以使用组实现以下操作:
将数量表示符应用到多个字符。
界定要应用逻辑"或"(通过使用 | 字符)的子模式。
捕获正则表达式中的子字符串匹配(例如,在正则表达式中使用 1 以匹配先前匹配的组,或类似地在 String 类的 replace() 方法中使用 $1)。
使用组捕获子字符串匹配
如果您在模式中定义标准括号组,则之后可以在正则表达式中引用它。这称为"逆向引用",并且此类型的组称为"捕获组"。
您可以通过键入 1,2,...,99 在正则表达式中指定最多 99 个此类逆向引用。
类似地,在 String 类的 replace() 方法中,可以使用 $1-$99 在替换字符串中插入捕获的组子字符串匹配。此外,如果使用捕获组,RegExp 类的 exec() 方法和 String 类的 match() 方法将返回与捕获组匹配的子字符串。
使用非捕获组和向前查找组
非捕获组是只用于分组的组,它不会被"收集",也不会匹配有限的逆向引用。可以使用 (?: 和 ) 来定义非捕获组。
一类特殊的非捕获组是"向前查找组",它包括两种类型:"正向前查找组"和"负向前查找组"。
使用 (?= 和 ) 定义正向前查找组,它指定组中的子模式位置必须匹配。但是,匹配正向前查找组的字符串部分可能匹配正则表达式中的剩余模式。
使用 (?! 和 ) 定义负向前查找组,它指定该组中的子模式位置必须不匹配。
使用命名组
命名组是正则表达式中给定命名标识符的一类组。使用 (?P<name> 和 ) 可定义命名组。
命名组不属于 ECMAScript 语言规范。它们是 ActionScript 3.0 中的新增功能。



标志和属性
下表列出了可以为正则表达式设置的五种标志。每种标志都可以作为正则表达式对象属性进行访问。
标志
属性
描述
g
global
匹配多个匹配。
i
ignoreCase
不区分大小写的匹配。应用于 A-Z 和 a-z 字符,但不能应用于扩展字符,如 E 和 e。
m
multiline
设置此标志后,^和$可以分别匹配行的开头和结尾。
s
dotall
设置此标志后,.(点)可以匹配换行符 (n)。
x
extended
允许扩展的正则表达式。您可以在正则表达式中键入空格,它将作为模式的一部分被忽略。这可使您更加清晰可读地键入正则表达式代码。
请注意这些属性都是只读属性。但是,您无法直接设置命名属性。
默认情况下,除非您在正则表达式声明中指定这些标志,否则不会设置,并且相应的属性也会设置为 false。
另外,还有其它两种正则表达式属性:
lastIndex 属性指定字符串中的索引位置以用于下次调用正则表达式的 exec() 或 test() 方法。
source 属性指定定义正则表达式的模式部分的字符串。
在m (multiline) 标志中,请注意,只有 n 字符表示行的结束。下列字符不表示行的结束:
回车 (r) 字符
Unicode 行分隔符 (u2028) 字符
Unicode 段分隔符 (u2029) 字符
对字符串使用正则表达式的方法
RegExp 类包含两个方法:exec() 和 test()。
除 RegExp 类的 exec() 和 test() 方法外,String 类还包含以下方法,使您可以在字符串中匹配正则表达式:match()、replace()、search() 和 splice()。
处理 XML
ActionScript 3.0 包含一组基于 ECMAScript for XML (E4X) 规范(ECMA-357 第 2 版)的类。这些类包含用于处理 XML 数据的强大且易用的功能。
XML 基础知识
处理 XML 简介
XML 是一种表示结构化信息的标准方法,以使计算机能够方便地使用此类信息,并且人们可以非常方便地编写和理解这些信息。XML 是 eXtensible Markup Language(可扩展标记语言)的缩写。
XML 提供了一种简便的标准方法对数据进行分类,以使其更易于读取、访问以及处理。XML 使用类似于 HTML 的树结构和标签结构。XML 数据也可能会比较复杂,其中包含嵌套在其它标签中的标签以及属性和其它结构组件。
XML 快速入门
XML 数据是以纯文本格式编写的,并使用特定语法将信息组织为结构化格式。通常,将一组 XML 数据称为"XML 文档"。在 XML 格式中,可通过分层结构将数据组织到"元素"(可以是单个数据项,也可以是其它元素的容器)中。每个 XML 文档将一个元素作为顶级项目或主项目;此根元素内可能会包含一条信息,但更可能会包含其它元素,而这些元素又包含其它元素,依此类推。
每个元素都是用一组"标签"来区分的,即包含在尖括号(小于号和大于号)中的元素名称。开始标签(指示元素的开头)包含元素名称:<title>结束标签(标记元素的结尾)在元素名称前面包含一个正斜杠:</title>如果元素不包含任何内容,则会将其编写为一个空元素(有时称为自结束元素)。在 XML 中,以下元素:<lastplayed/>与下面的元素完全相同:<lastplayed></lastplayed>。
除了在开始和结束标签之间包含的元素内容外,元素还可以包含在元素开始标签中定义的其它值(称为"属性")。
每个 XML 元素都包含内容,这可以是单个值、一个或多个 XML 元素或没有任何内容(对于空元素)。
用于处理 XML 的 ActionScript 类
ActionScript 3.0 包含一些用于处理 XML 结构化信息的类。下面列出了两个主类:
XML:表示单个 XML 元素,它可以是包含多个子元素的 XML 文档,也可以是文档中的单值元素。
XMLList:表示一组 XML 元素。当具有多个"同级"(在 XML 文档分层结构中的相同级别,并且包含在相同父级中)的 XML 元素时,将使用 XMLList 对象。
对于涉及 XML 命名空间的更高级用法,ActionScript 还包含 Namespace 和 QName 类。
除了用于处理 XML 的内置类外,ActionScript 3.0 还包含一些运算符,它们提供了用于访问和处理 XML 数据的特定功能。这种使用这些类和运算符来处理 XML 的方法称为 ECMAScript for XML (E4X),它是由 ECMA-357 第 2 版规范定义的。
重要概念和术语
元素 (Element):XML 文档中的单个项目,它被标识为开始标签和结束标签之间包含的内容(包括标签)。XML 元素可以包含文本数据或其它元素,也可以为空。
空元素 (Empty element):不包含任何子元素的 XML 元素。通常,将空元素编写为自结束标签(如 <element/>)。
文档 (Document):单个 XML 结构。XML 文档可以包含任意数量的元素(或者仅包含单个空元素);但是,XML 文档必须具有一个顶级元素,该元素包含文档中的所有其它元素。
节点 (Node):XML 元素的另一种称谓。
属性 (Attribute):与元素关联的命名值,它以 attributename="value" 格式写入到元素的开始标签中,而不是编写为嵌套在元素内的单独子元素。
用于处理 XML 的 E4X 方法
ECMAScript for XML 规范定义了一组用于处理 XML 数据的类和功能。这些类和功能统称为 E4X。ActionScript 3.0 包含以下 E4X 类:XML、XMLList、QName 和 Namespace。
E4X 包含了一些直观运算符(如点 (.))和属性标识符 (@) 运算符,用于访问 XML 中的属性 (property) 和属性 (attribute)。使用 @ 和 . 运算符不仅可以读取数据,还可以分配数据。
XML 对象
XML 对象可能表示 XML 元素、属性、注释、处理指令或文本元素。
XML 对象分为包含"简单内容"和包含"复杂内容"两类。有子节点的 XML 对象归入包含复杂内容的一类。如果 XML 对象是属性、注释、处理指令或文本节点之中的任何一个,我们就说它包含简单内容。
XML 属性
XML 类有五个静态属性:
ignoreComments 和 ignoreProcessingInstructions 属性确定分析 XML 对象时是否忽略注释或处理指令。
ignoreWhitespace 属性确定在只由空白字符分隔的元素标签和内嵌表达式中是否忽略空白字符。
prettyIndent 和 prettyPrinting 属性用于设置由 XML 类的 toString() 和 toXMLString() 方法返回的文本的格式。
XML 方法
以下方法用于处理 XML 对象的分层结构:
appendChild()、 child()、 childIndex()、 children()、descendants()、elements()、 insertChildAfter()、
insertChildBefore()、 parent()、 prependChild()。
以下方法用于处理 XML 对象属性 (attribute):
attribute()、 attributes() 。
以下方法用于处理 XML 对象属性 (property):
hasOwnProperty()、 propertyIsEnumerable()、 replace()、 setChildren()。
以下方法用于处理限定名和命名空间:
addNamespace()、 inScopeNamespaces()、 localName()、 name()、 namespace()、 namespaceDeclarations()、
removeNamespace()、 setLocalName()、 setName()、 setNamespace()。
以下方法用于处理和确定某些类型的 XML 内容:
comments()、 hasComplexContent()、 hasSimpleContent()、 nodeKind()、 processingInstructions()、
text()。
以下方法用于转换为字符串和设置 XML 对象的格式:
defaultSettings()、 setSettings()、 settings()、 normalize()、 toString()、 toXMLString()。
还有其它几个方法:
contains()、 copy()、 valueOf()、 length()。
XMLList 对象
XMLList 实例表示 XML 对象的任意集合。它可以包含完整的 XML 文档、XML 片断或 XML 查询结果。
以下方法用于处理 XMLList 对象的分层结构:
child()、 children()、 descendants()、 elements()、 parent()。
以下方法用于处理 XMLList 对象属性 (attribute):
attribute()、 attributes()。
以下方法用于处理 XMLList 属性 (property):
hasOwnProperty()、 propertyIsEnumerable()。
以下方法用于处理和确定某些类型的 XML 内容:
comments()、 hasComplexContent()、 hasSimpleContent()、 processingInstructions()、 text()。
以下方法用于转换为字符串和设置 XMLList 对象的格式:
normalize()、 toString()、 toXMLString()。
还有其它几个方法:
contains()、 copy()、 length()、 valueOf()。
对于只包含一个 XML 元素的 XMLList 对象,可以使用 XML 类的所有属性和方法,因为包含一个 XML 元素的 XMLList 被视为等同于 XML 对象。
组合和变换 XML 对象
使用 prependChild() 方法或 appendChild() 方法可在 XML 对象属性列表的开头或结尾添加属性。
使用 insertChildBefore() 方法或 insertChildAfter() 方法在指定属性之前或之后添加属性。
可以使用 = 运算符将属性 (property) 和属性 (attribute) 赋予 XML 对象,
如下所示:
var x:XML =
<employee>
<lastname>Smith</lastname>
</employee>
x.firstname = "Jean";
x.@id = "239";
这将对 XML 对象 x 进行如下设置:
<employee id="239">
<lastname>Smith</lastname>
<firstname>Jean</firstname>
</employee>

可以使用 + 和 += 运算符连接 XMLList 对象。
遍历 XML 结构
XML 的一个强大功能是它能够通过文本字符的线性字符串提供复杂的嵌套数据。将数据加载到 XML 对象时,ActionScript 会分析数据并将其分层结构加载到内存(如果 XML 数据格式有误,它会发送运行时错误)。
利用 XML 和 XMLList 对象的运算符和方法可以轻松遍历 XML 数据的结构。
使用点 (.) 运算符和后代存取器 (..) 运算符可以访问 XML 对象的子属性。
访问父节点和子节点
parent() 方法返回 XML 对象的父项。
可以使用子级列表的序数索引值访问特定的子对象。要访问特定的孙项,可为子项和孙项名称同时指定索引编号。可以使用 child() 方法导航到名称基于变量或表达式的子项。
访问属性
使用 @ 符号(属性标识符运算符)可以访问 XML 或 XMLList 对象的属性,可以一起使用 * 通配符和 @ 符号来访问 XML 或 XMLList 对象的所有属性,可以使用 attribute() 或 attributes() 方法访问 XML 或 XMLList 对象的特定属性或所有属性。
按属性或元素值过滤
可以使用括号运算符 -- ( 和 ) -- 过滤具有特定元素名称或属性值的元素。
使用 for..in 和 for each..in 语句
ActionScript 3.0 包含用于循环访问 XMLList 对象的 for..in 语句和 for each..in 语句。
使用 XML 命名空间
XML 对象(或文档)中的命名空间用于标识对象所包含的数据的类型。
XML 类包含用于处理命名空间的以下方法:addNamespace()、inScopeNamespaces()、localName()、name()、namespace()、namespaceDeclarations()、removeNamespace()、setLocalName()、setName() 和 setNamespace()。
default xml namespace 指令用于为 XML 对象指定默认的命名空间。
XML 类型转换
可以将 XML 对象和 XMLList 对象转换为字符串值。同样,也可以将字符串转换为 XML 对象和 XMLList 对象。还要记住,所有 XML 属性值、名称和文本值都是字符串。
将 XML 和 XMLList 对象转换为字符串
XML 和 XMLList 类都包含一个 toString() 方法和一个 toXMLString() 方法。toXMLString() 方法返回包含该 XML 对象的所有标签、属性、命名空间声明和内容的字符串。对于包含复杂内容(子元素)的 XML 对象,toString() 方法的作用与 toXMLString() 方法完全相同。对于包含简单内容的 XML 对象(只包含一个文本元素的对象),toString() 方法只返回该元素的文本内容.
如果使用 trace() 方法但不指定 toString() 或 toXMLString(),则默认情况下将使用 toString() 方法转换数据. 使用 trace() 方法调试代码时,通常都要使用 toXMLString() 方法,以便 trace() 方法输出更完整的数据。
将字符串转换为 XML 对象
可以使用 new XML() 构造函数从字符串创建 XML 对象,如果试图将表示无效 XML 或格式有误的 XML 的字符串转换为 XML,则会引发运行时错误.
显示编程
ActionScript 3.0 中的显示编程用于处理出现在 Adobe Flash Player 9 的舞台上的元素。
显示编程的基础知识
显示编程简介
使用 ActionScript 3.0 构建的每个应用程序都有一个由显示对象构成的层次结构,这个结构称为"显示列表"。显示列表包含应用程序中的所有可视元素。显示元素属于下列一个或多个组:
舞台
舞台是包括显示对象的基础容器。每个应用程序都有一个 Stage 对象,其中包含所有的屏幕显示对象。舞台是顶级容器,它位于显示列表层次结构的顶部:

每个 SWF 文件都有一个关联的 ActionScript 类,称为"SWF 文件的主类"。当 Flash Player 在 HTML 页中打开 SWF 文件时,Flash Player 将调用该类的构造函数,所创建的实例(始终是一种显示对象)将添加为 Stage 对象的子级。SWF 文件的主类始终扩展 Sprite 类.
可以通过任何 DisplayObject 实例的 stage 属性来访问舞台。
显示对象
在 ActionScript 3.0 中,在应用程序屏幕上出现的所有元素都属于"显示对象"类型。flash.display 包中包括的 DisplayObject 类是由许多其它类扩展的基类。这些不同的类表示一些不同类型的显示对象,如矢量形状、影片剪辑和文本字段等。
显示对象容器
显示对象容器是一些特殊类型的显示对象,这些显示对象除了有自己的可视表示形式之外,还可以包含也是显示对象的子对象。
DisplayObjectContainer 类是 DisplayObject 类的子类。DisplayObjectContainer 对象可以在其"子级列表"中包含多个显示对象。在讨论显示对象的上下文中,DisplayObjectContainer 对象又称为"显示对象容器"或简称为"容器"。
尽管所有可视显示对象都从 DisplayObject 类继承,但每类显示对象都是 DisplayObject 类的一个特定子类。例如,有 Shape 类或 Video 类的构造函数,但没有 DisplayObject 类的构造函数。
如前所述,舞台是显示对象容器。
重要概念和术语
Alpha:表示颜色中的透明度(更准确地说,是不透明度)的颜色值。
位图图形 (Bitmap graphic):在计算机中定义为彩色像素网格(行和列)的图形。
混合模式 (Blending mode):指定两个重叠图像的内容应如何进行交互。通常,一个图像上面的另一个不透明图像会遮盖住下面的图像,因此根本看不到该图像;但是,不同的混合模式会导致图像颜色以不同方式混合在一起,因此,生成的内容是两个图像的某种组合形式。
显示列表 (Display list):由 Flash Player 呈现为可见屏幕内容的显示对象的层次结构。舞台是显示列表的根,附加到舞台或其子级之一上的所有显示对象构成了显示列表(即使并未实际呈现该对象,例如,如果它位于舞台边界以外)。
显示对象 (Display object):在 Flash Player 中表示某种类型的可视内容的对象。显示列表中只能包含显示对象,所有显示对象类是 DisplayObject 类的子类。
显示对象容器 (Display object container):一种特殊类型的显示对象,除了(通常)具有其自己的可视表示形式以外,它还可以包含子显示对象。
SWF 文件的主类 (Main class of the SWF file):为 SWF 文件中最外面的显示对象定义行为的类,从概念上讲,它是 SWF 文件本身的类。例如,在 Flash 创作环境中创建的 SWF 具有一个"主时间轴",它包含所有其它的时间轴;SWF 文件的主类是指将主时间轴作为其实例的类。
蒙版 (Masking):一种将图像的某些部分隐藏起来(或者相反,只允许显示图像的某些部分)的技术。图像的隐藏部分将变为透明,因此,将显示其下面的内容。
舞台 (Stage):一个可视容器,它是 SWF 文件中的所有可视内容的基础或背景。
变形 (Transformation):对图形的可视特性进行的调整.
矢量图形 (Vector graphic):在计算机中定义为使用特定特性(如粗细、长度、大小、角度以及位置)绘制的线条和形状的图形。
核心显示类
ActionScript 3.0 的 flash.display 包中包括可在 Flash Player 中显示的可视对象的类。下图说明了这些核心显示对象类的子类关系。

该图说明了显示对象类的类继承。请注意,其中某些类,尤其是 StaticText、TextField 和 Video 类,不在 flash.display 包中,但它们仍然是从 DisplayObject 类继承的。
扩展 DisplayObject 类的所有类都继承该类的方法和属性。
可以实例化包含在 flash.display 包中的下列类的对象:
Bitmap ─ 使用 Bitmap 类可定义从外部文件加载或通过 ActionScript 呈现的位图对象。可以通过 Loader 类从外部文件加载位图。可以加载 GIF、JPG 或 PNG 文件。还可以创建包含自定义数据的 BitmapData 对象,然后创建使用该数据的 Bitmap 对象。可以使用 BitmapData 类的方法来更改位图,无论这些位图是加载的还是在 ActionScript 中创建的。
Loader ─ 使用 Loader 类可加载外部资源(SWF 文件或图形)。
Shape ─ 使用 Shape 类可创建矢量图形,如矩形、直线、圆等。
SimpleButton ─ SimpleButton 对象是 Flash 按钮元件的 ActionScript 表示形式。SimpleButton 实例有 3 个按钮状态:弹起、按下和指针经过。
Sprite ─ Sprite 对象可以包含它自己的图形,还可以包含子显示对象。(Sprite 类用于扩展 DisplayObjectContainer 类)。
MovieClip ─ MovieClip 对象是在 Flash 创作工具中创建的 ActionScript 形式的影片剪辑元件。实际上,MovieClip 与 Sprite 对象类似,不同的是它还有一个时间轴。
下列类不在 flash.display 包中,这些类是 DisplayObject 类的子类:
TextField 类包括在 flash.text 包中,它是用于文本显示和输入的显示对象。
Video 类包括在 flash.media 包中,它是用于显示视频文件的显示对象。
flash.display 包中的下列类用于扩展 DisplayObject 类,但您不能创建这些类的实例。这些类而是用作其它显示对象的父类,因此可将通用功能合并到一个类中。
AVM1Movie ─ AVM1Movie 类用于表示在 ActionScript 1.0 和 2.0 中创作的已加载 SWF 文件。
DisplayObjectContainer ─ Loader、Stage、Sprite 和 MovieClip 类每个都用于扩展了 DisplayObjectContainer 类。
InteractiveObject ─ InteractiveObject 是用于与鼠标和键盘交互的所有对象的基类。SimpleButton、TextField、Video、Loader、Sprite、Stage 和 MovieClip 对象是 InteractiveObject 类的所有子类。
MorphShape ─ 这些对象是在 Flash 创作工具中创建补间形状时创建的。无法使用 ActionScript 实例化这些对象,但可以从显示列表中访问它们。
Stage ─ Stage 类用于扩展 DisplayObjectContainer 类。有一个应用程序的 Stage 实例,该实例位于显示列表层次结构的顶部。要访问 Stage,请使用任何 DisplayObject 实例的 stage 属性。
此外,flash.text 包中的 StaticText 类也用于扩展 DisplayObject 类,但不能在代码中创建它的实例。只能在 Adobe Flash CS3 Professional 中创建静态文本字段。
处理显示对象
DisplayObject 类的属性和方法
所有显示对象都是 DisplayObject 类的子类,同样它们还会继承 DisplayObject 类的属性和方法。继承的属性是适用于所有显示对象的基本属性。
您不能使用 DisplayObject 类构造函数来创建 DisplayObject 实例。必须创建另一种对象(属于 DisplayObject 类的子类的对象,如 Sprite)才能使用 new 运算符来实例化对象。此外,如果要创建自定义显示对象类,还必须创建具有可用构造函数的其中一个显示对象子类的子类(如 Shape 类或 Sprite 类)。
在显示列表中添加显示对象
实例化显示对象时,在将显示对象实例添加到显示列表上的显示对象容器之前,显示对象不会出现屏幕上(即在舞台上)。
当在舞台上添加任何可视元素时,该元素会成为 Stage 对象的"子级"。应用程序中加载的第一个 SWF 文件(例如,HTML 页中嵌入的文件)会自动添加为 Stage 的子级。它可以是扩展 Sprite 类的任何类型的对象。
"不是"使用 ActionScript 创建的任何显示对象(例如,通过在 Adobe Flex Builder 2 中添加 MXML 标签或在 Flash 的舞台上放置某项而创建任何对象),都会添加到显示列表中。尽管没有通过 ActionScript 添加这些显示对象,但仍可通过 ActionScript 访问它们。
处理显示对象容器
如果从显示列表中删除某个 DisplayObjectContainer 对象,或者以其它某种方式移动该对象或对其进行变形处理,则会同时删除、移动 DisplayObjectContainer 中的每个显示对象或对其进行变形处理。
显示对象容器本身就是一种显示对象,它可以添加到其它显示对象容器中。
要使某一显示对象出现在显示列表中,必须将该显示对象添加到显示列表上的显示对象容器中。使用容器对象的 addChild() 方法或 addChildAt() 方法可执行此操作。
使用 addChildAt() 方法可将子级添加到显示对象容器的子级列表中的特定位置。子级列表中这些从 0 开始的索引位置与显示对象的分层(从前到后顺序)有关。要重新将对象定位到显示列表的顶部,只需重新将其添加到列表中。
可以使用 getChildAt() 方法来验证显示对象的图层顺序。getChildAt() 方法根据您向容器传递的索引编号返回容器的子对象。
如果从父容器的子级列表中删除了一个显示对象,则列表中位置较高的每一个元素在子索引中会分别下移一个位置。removeChild() 和 removeChildAt() 方法并不完全删除显示对象实例。这两种方法只是从容器的子级列表中删除显示对象实例。该实例仍可由另一个变量引用。(请使用 delete 运算符完全删除对象。)
由于显示对象只有一个父容器,因此只能在一个显示对象容器中添加显示对象的实例。如果将在第一个显示对象容器中包含的某一显示对象添加到另一个显示对象容器中,则会从第一个显示对象容器的子级列表中删除该显示对象。
DisplayObjectContainer 类还定义了用于处理子显示对象的几个方法,其中包括:
contains():确定显示对象是否是 DisplayObjectContainer 的子级。
getChildByName():按名称检索显示对象。
getChildIndex():返回显示对象的索引位置。
setChildIndex():更改子显示对象的位置。
swapChildren():交换两个显示对象的前后顺序。
swapChildrenAt():交换两个显示对象的前后顺序(由其索引值指定)。
遍历显示列表
正如所看到的,显示列表是一个树结构。树的顶部是舞台,它可以包含多个显示对象。那些本身就是显示对象容器的显示对象可以包含其它显示对象或显示对象容器。


DisplayObjectContainer 类包括通过显示对象容器的子级列表遍历显示列表的属性和方法。getChildAt() 方法返回显示列表中特定索引位置的子级。您也可以按名称访问子对象。每个显示对象都有一个名称属性,如果没有指定该属性,Flash Player 会指定一个默认值,如 "instance1"。使用 getChildByName() 方法来访问具有名称属性的子显示对象。与使用 getChildAt() 方法相比,使用 getChildByName() 方法会导致性能降低。
由于显示对象容器可以包含其它显示对象容器作为其显示列表中的子对象,因此您可将应用程序的完整显示列表作为树来遍历。
设置舞台属性
Stage 类用于覆盖 DisplayObject 类的大多数属性和方法。如果调用其中一个已覆盖的属性或方法,Flash Player 会引发异常。
如果显示对象与加载的第一个 SWF 文件不在同一个安全沙箱中,则 Stage 类的某些属性和方法不适用于这些显示对象。
控制回放帧速率
Stage 类的 framerate 属性用于设置加载到应用程序中的所有 SWF 文件的帧速率。
控制舞台缩放比例
当调整 Flash Player 屏幕的大小时,Flash Player 会自动调整舞台内容来加以补偿。Stage 类的 scaleMode 属性可确定如何调整舞台内容。此属性可以设置为四个不同值。
对于scaleMode 的三个值(StageScaleMode.EXACT_FIT、StageScaleMode.SHOW_ALL 和 StageScaleMode.NO_BORDER),Flash Player 将缩放舞台的内容以容纳在舞台边界内。三个选项在确定如何完成缩放时是不相同的。
StageScaleMode.EXACT_FIT 按比例缩放 SWF。
StageScaleMode.SHOW_ALL 确定是否显示边框(就像在标准电视上观看宽屏电影时显示的黑条)。
StageScaleMode.NO_BORDER 确定是否可以部分裁切内容。
或者,如果将 scaleMode 设置为 StageScaleMode.NO_SCALE,则当查看者调整 Flash Player 窗口大小时,舞台内容将保持定义的大小。仅在缩放模式中,Stage 类的 width 和 height 属性才可用于确定 Flash Player 窗口调整大小后的实际像素尺寸。(在其它缩放模式中,stageWidth 和 stageHeight 属性始终反映的是 SWF 的原始宽度和高度。)此外,当 scaleMode 设置为 StageScaleMode.NO_SCALE 并且调整了 SWF 文件大小时,将调度 Stage 类的 resize 事件,允许您进行相应地调整。
因此,将 scaleMode 设置为 StageScaleMode.NO_SCALE 可以更好地控制如何根据需要调整屏幕内容以适合窗口大小。
处理全屏模式
使用全屏模式可令 SWF 填充查看器的整个显示器,没有任何边框、菜单栏等。Stage 类的 displayState 属性用于切换 SWF 的全屏模式。可以将 displayState 属性设置为由 flash.display.StageDisplayState 类中的常量定义的其中一个值。要打开全屏模式,请将 displayState 设置为 StageDisplayState.FULL_SCREEN。
要退出全屏模式,请将 displayState 属性设置为 StageDisplayState.NORMAL。
此外,用户可以通过将焦点切换到其它窗口或使用以下组合键之一来选择退出全屏模式:Esc(所有平台)、Ctrl-W (Windows)、Command-W (Mac) 或 Alt-F4 (Windows)。
全屏模式的舞台缩放行为与正常模式下的相同;缩放比例由 Stage 类的 scaleMode 属性控制。通常,如果将 scaleMode 属性设置为 StageScaleMode.NO_SCALE,则 Stage 的 stageWidth 和 stageHeight 属性将发生更改,以反映由 SWF 占用的屏幕区域的大小(在本例中为整个屏幕)。
打开或关闭全屏模式时,可以使用 Stage 类的 fullScreen 事件来检测和响应。fullScreen 事件的事件对象是 flash.events.FullScreenEvent 类的实例,它包含指示是启用 (true) 还是禁用 (false) 全屏模式的 fullScreen 属性。
在 ActionScript 中处理全屏模式时,需要记住以下注意事项:
只能通过 ActionScript 响应鼠标单击(包括右键单击)或按键才能启动全屏模式。
对于有多个显示器的用户,SWF 内容将展开且只填充一个显示器。Flash Player 使用度量信息来确定哪
个显示器包含 SWF 的最大部分内容,然后使用该显示器提供全屏模式。
对于 HTML 页中嵌入的 SWF 文件,嵌入 Flash Player 的 HTML 代码必须包括名为 allowFullScreen 且值为 true 的 param 标签和 embed 属性,如下所示:
<object>
...
<param name="allowFullScreen" value="true" />
<embed ... allowfullscreen="true" />
</object>
如果要在网页中使用 JavaScript 来生成 SWF 嵌入标签,则必须更改 JavaScript 以添加 allowFullScreen param 标签和属性。例如,如果 HTML 页使用 AC_FL_RunContent() 函数(由 Flex Builder 和 Flash 生成的 HTML 页使用),则应在该函数调用中添加 allowFullScreen 参数,如下所示:
AC_FL_RunContent(
...
'allowFullScreen','true',
...
); //end AC code
这不适用于在独立 Flash Player 中运行的 SWF 文件。
在全屏模式下将禁用所有与键盘有关的 ActionScript,如 TextField 实例中的键盘事件和文本输入。用
于关闭全屏模式的键盘快捷键除外。
处理显示对象的事件
DisplayObject 类从 EventDispatcher 类继承。这意味着,每个显示对象都可完全参与到事件模型中。每个显示对象可使用其 addEventListener() 方法(继承自 EventDispatcher 类)来侦听特定的事件,但只有侦听对象是该事件的事件流的一部分时才能实现。
当 Flash Player 调度某个事件对象时,该事件对象会执行从舞台到发生事件的显示对象的往返行程。
处理显示对象事件时需要记住的一个重要问题是:从显示列表中删除显示对象时,事件侦听器的存在将会对是否从内存中自动删除显示对象(垃圾回收)产生影响。如果显示对象拥有订阅为其事件的侦听器的对象,即使从显示列表中删除了显示对象,也不会从内存中删除显示对象,因为显示对象仍然拥有到这些侦听器对象的引用。
选择 DisplayObject 子类
如果有多个可供选择的选项,处理显示对象时要作出的一个重要决策是:每个显示对象的用途是什么。以下原则可以帮助您作出决策。无论是需要类实例,还是选择要创建的类的基类,都可以应用这些建议:
如果不需要可作为其它显示对象的容器的对象(即只需要用作独立屏幕元素的对象),请根据使用目的选择 DisplayObject 或 InteractiveObject 两个子类中的一个:
用于显示位图图像的 Bitmap。
用于添加文本的 TextField。
用于显示视频的 Video。
用于绘制屏幕内容的"画布"的 Shape。特别是,如果要创建用于在屏幕上绘制形状的实例,而且该实例不是其它显示对象的容器,则使用 Shape 比使用 Sprite 或 MovieClip 有明显的性能优势。
用于 Flash 具体创作项的 MorphShape、StaticText 或 SimpleButton。(无法以编程方式创建这些类的实例,但可以通过创建这些数据类型的变量来引用使用 Flash 创作程序创建的项目。)
如果需要使用变量来引用主舞台,请使用 Stage 类作为其数据类型。
如果需要容器来加载外部 SWF 文件或图像文件,请使用 Loader 实例。加载的内容将作为 Loader 实例的子级添加到显示列表中。其数据类型将取决于加载内容的性质,如下所示:
加载的图像将是 Bitmap 实例。
使用 ActionScript 3.0 编写的已加载 SWF 文件将是 Sprite 或 MovieClip 实例(或这些类的子类的实例,由内容创建者指定)。
使用 ActionScript 1.0 或 ActionScript 2.0 编写的已加载 SWF 文件将是 AVM1Movie 实例。
如果需要将一个对象用作其它显示对象的容器(无论是否还要使用 ActionScript 在显示对象上进行绘制),请选择其中一个 DisplayObjectContainer 子类:
如果对象是只使用 ActionScript 创建的,或者如果对象作为只使用 ActionScript 创建和处理的自定义显示对象的基类,请选择 Sprite。
如果要通过创建变量来引用在 Flash 创作工具中创建的影片剪辑元件,请选择 MovieClip。
如果要创建的类与 Flash 库中的影片剪辑元件关联,请选择其中一个 DisplayObjectContainer 子类作为该类的基类:
如果关联的影片剪辑元件在多个帧上有内容,请选择 MovieClip
如果关联的影片剪辑元件仅在第一帧上有内容,请选择 Sprite
处理显示对象
改变位置
对任何显示对象进行的最基本操作是确定显示对象在屏幕上的位置。要设置显示对象的位置,请更改对象的 x 和 y 属性。
显示对象定位系统将舞台视为一个笛卡尔坐标系(带有水平 x 轴和垂直 y 轴的常见网格系统)。坐标系的原点(x 和 y 轴相交的 0,0 坐标)位于舞台的左上角。从原点开始,x 轴的值向右为正,向左为负,而 y 轴的值向下为正,向上为负(与典型的图形系统相反)。
默认情况下,当使用 ActionScript 创建显示对象时,x 和 y 属性均设置为 0,从而可将对象放在其父内容的左上角。
改变相对于舞台的位置
一定要记住 x 和 y 属性始终是指显示对象相对于其父显示对象坐标轴的 0,0 坐标的位置,这一点很重要。因此,对于包含在 Sprite 实例内的 Shape 实例(如圆),如果将 Shape 对象的 x 和 y 属性设置为 0,则会将圆放在 Sprite 的左上角,该位置不一定是舞台的左上角。要确定对象相对于全局舞台坐标的位置,可以使用任何显示对象的 globalToLocal() 方法将坐标从局部(舞台)坐标转换为本地(显示对象容器)坐标。同样,可以使用 DisplayObject 类的 localToGlobal() 方法将本地坐标转换为舞台坐标。
创建拖放交互组件
移动显示对象的一个常见理由是要创建拖放交互组件,这样当用户单击某个对象时,在松开鼠标按键之前,该对象会随着鼠标的移动而移动。在 ActionScript 中可以采用两种方法创建拖放交互组件。在每种情况下,都会使用两个鼠标事件:按下鼠标按键时,通知对象跟随鼠标光标;松开鼠标按键时,通知对象停止跟随鼠标光标。
第一方法使用 startDrag() 方法,它比较简单,但限制较多。按下鼠标按键时,将调用要拖动的显示对象的 startDrag() 方法。松开鼠标按键时,将调用 stopDrag() 方法。
这种方法有一个非常大的限制:每次只能使用 startDrag() 拖动一个项目。如果正在拖动一个显示对象,然后对另一个显示对象调用了 startDrag() 方法,则第一个显示对象会立即停止跟随鼠标。
由于每次只能使用 startDrag() 拖动一个对象,因此,可以对任何显示对象调用 stopDrag() 方法,这会停止当前正在拖动的任何对象。
如果需要拖动多个显示对象,或者为了避免多个对象可能会使用 startDrag() 而发生冲突,最好使用鼠标跟随方法来创建拖动效果。通过这种技术,当按下鼠标按键时,会将函数作为舞台的 mouseMove 事件的侦听器来订阅。然后,每次鼠标移动时都会调用此函数,它将使所拖动的对象跳到鼠标所在的 x,y 坐标。松开鼠标按键后,取消此函数作为侦听器的订阅,这意味着鼠标移动时不再调用该函数且对象停止跟随鼠标光标。
除了使显示对象跟随鼠标光标之外,拖放交互组件的共有部分还包括将拖动对象移到显示对象的前面,以使拖动对象好像浮动在所有其它对象之上。
最后,要增强效果,您可以在单击显示对象时(开始拖动显示对象时)对显示对象应用投影滤镜,然后在松开对象时删除投影。
平移和滚动显示对象
如果显示对象太大,不能在要显示它的区域中完全显示出来,则可以使用 scrollRect 属性定义显示对象的可查看区域。此外,通过更改 scrollRect 属性响应用户输入,可以使内容左右平移或上下滚动。
scrollRect 属性是 Rectangle 类的实例,Rectangle 类包括将矩形区域定义为单个对象所需的有关值。最初定义显示对象的可查看区域时,请创建一个新的 Rectangle 实例并为该实例分配显示对象的 scrollRect 属性。以后进行滚动或平移时,可以将 scrollRect 属性读入单独的 Rectangle 变量,然后更改所需的属性(例如,更改 Rectangle 实例的 x 属性进行平移,或更改 y 属性进行滚动)。然后将该 Rectangle 实例重新分配给 scrollRect 属性,将更改的值通知显示对象。
使用显示对象的 scrollRect 属性时,最好指定 Flash Player 应使用 cacheAsBitmap 属性将显示对象的内容作为位图来缓存。如果这样做了,每次滚动显示对象时,Flash Player 就不必重绘显示对象的整个内容,而可以改为使用缓存的位图将所需部分直接呈现到屏幕上。
处理大小和缩放对象
您可以采用两种方法来测量和处理显示对象的大小:使用尺寸属性(width 和 height)或缩放属性(scaleX 和 scaleY)。
每个显示对象都有 width 属性和 height 属性,它们最初设置为对象的大小,以像素为单位。您可以通过读取这些属性的值来确定显示对象的大小。还可以指定新值来更改对象的大小。
更改显示对象的 height 或 width 会导致缩放对象,这意味着对象内容经过伸展或挤压以适合新区域的大小。如果显示对象仅包含矢量形状,将按新缩放比例重绘这些形状,而品质不变。此时将缩放显示对象中的所有位图图形元素,而不是重绘。
当更改显示对象的 width 或 height 属性时,Flash Player 还会更新对象的 scaleX 和 scaleY 属性。这些属性表示显示对象与其原始大小相比的相对大小。scaleX 和 scaleY 属性使用小数(十进制)值来表示百分比。
如果更改一个正方形的 height 但不更改其 width,则其边长不再相同,它将是一个矩形而不是一个正方形。如果要更改显示对象的相对大小,则可以通过设置 scaleX 和 scaleY 属性的值来调整该对象的大小,另一种方法是设置 width 或 height 属性。
控制缩放时的扭曲
使用 9 切片缩放 (Scale-9) 来创建在其中控制如何缩放对象的显示对象。使用 9 切片缩放时,显示对象被分成 9 个单独的矩形(一个 3 x 3 的网格,就像一个“井”字)。矩形的大小不必一定相同,您可以指定放置网格线的位置。缩放显示对象时,四个角矩形中的任何内容(如按钮的圆角)不伸展也不压缩。上中矩形和下中矩形将进行水平缩放,但不进行垂直缩放,而左中矩形和右中矩形将进行垂直缩放,但不进行水平缩放。中心矩形既进行水平缩放又进行垂直缩放。


请记住,如果要创建显示对象且希望某些内容从不缩放,只需要通过放置 9 切片缩放网格的划分线来确保有关内容完全放在其中一个角矩形中即可。
在 ActionScript 中,如果为显示对象的 scale9Grid 属性设置一个值,就会打开对象的 9 切片缩放并定义对象的 Scale-9 网格中矩形的大小。可以使用 Rectangle 类的实例作为 scale9Grid 属性的值,如下所示:
myButton.scale9Grid = new Rectangle(32, 27, 71, 64);
Rectangle 构造函数的四个参数是 x 坐标、y 坐标、width 和 height。本示例中,矩形的左上角放在名为 myButton 的显示对象上的点 x:32,y:27 处。矩形宽 71 个像素,高 65 个像素(因此其右边位于显示对象上 x 坐标为 103 的位置,其下边位于显示对象上 y 坐标为 92 的位置)。


包含在由 Rectangle 实例定义的区域中的实际区域表示 Scale-9 网格的中心矩形。其它矩形是由 Flash Player 通过扩展 Rectangle 实例的各边计算出来的,如下所示:


在本例中,当按钮放大或缩小时,圆角不拉伸也不压缩,但其它区域将通过调整来适应缩放。


缓存显示对象
可以通过缓存指定的显示对象来提高 SWF 文件的性能。显示对象是一个"表面",实际上是位图版本的实例矢量数据,矢量数据是 SWF 文件中不需要有太多更改的一种数据。因此,打开缓存的实例不会随 SWF 文件的播放而不断地重绘,这样便可快速呈现 SWF 文件。
可以更新矢量数据,这时将重新创建表面。因此,缓存在表面中的矢量数据不需要在整个 SWF 文件中保持一样。
将显示对象的 cacheAsBitmap 属性设置为 true 会使显示对象缓存其自身的位图表示。Flash 为该实例创建一个 surface 对象,该对象是一个缓存的位图,而不是矢量数据。如果要更改显示对象的边界,则重新创建表面而不是调整其大小。表面可以嵌套在其它表面之内。子表面会将其位图复制到它的父表面上。
DisplayObject 类的 opaqueBackground 属性和 scrollRect 属性与使用 cacheAsBitmap 属性的位图缓存有关。尽管这三个属性彼此互相独立,但是,当对象缓存为位图时,opaqueBackground 和 scrollRect 属性的作用最佳,只有将 cacheAsBitmap 设置为 true 时,才能看到 opaqueBackground 和 scrollRect 属性带来的性能优势。
何时启用缓存
对显示对象启用缓存可创建表面,表面具有助于更快地呈现复杂的矢量动画等优点。
缓存数据的总体性能取决于实例矢量数据的复杂程度、要更改的数据量,以及是否设置了 opaqueBackground 属性。
何时使用位图缓存
在以下典型情况下,启用位图缓存可能会带来明显的好处。
复杂的背景图像:应用程序包含由矢量数据组成的细节丰富且背景复杂的图像。您可能会在背景上设计动画人物,这会降低动画的速度,因为背景需要持续地重新生成矢量数据。要提高性能,可以将背景显示对象的 opaqueBackground 属性设置为 true。背景将呈现为位图,可以迅速地重绘,所以动画的播放速度比较快。
滚动文本字段:应用程序在滚动文本字段中显示大量的文本。可以将文本字段放置在您设置为可滚动的具有滚动框(使用 scrollRect 属性)的显示对象中。这可以使指定的实例进行快速像素滚动。当用户滚动显示对象实例时,Flash 通过将滚动的像素向上移来生成新的看得见的区域,而不是重新生成整个文本字段。
窗口排列秩序:应用程序具有秩序复杂的重叠窗口。每个窗口都可以打开或关闭。如果将每个窗口标记为一个表面(将 cacheAsBitmap 属性设置为 true),则各个窗口将隔离开来进行缓存。用户可以拖动窗口使其互相重叠,每个窗口并不重新生成矢量内容。
Alpha 通道遮罩:当使用 Alpha 通道遮罩时,必须将 cacheAsBitmap 属性设置为 true。
所有这些情况下,启用位图缓存后都通过优化矢量图来提高应用程序的响应能力和互动性。
此外,只要对显示对象应用滤镜,Flash Player 就会将 cacheAsBitmap 自动设置为 true,即使已明确将其设置为 false 也是如此。如果清除了显示对象的所有滤镜,则 cacheAsBitmap 属性会返回最后设置的值。
何时避免使用位图缓存
滥用此功能对 SWF 文件可能会有负面影响。使用位图缓存时,请记住下面的准则:
不要过度使用表面(启用了缓存的显示对象)。每个表面使用的内存都比常规显示对象多,这意味着只在需要提高呈现性能时才启用表面。
避免放大缓存的表面。如果过度使用位图缓存,尤其是放大内容时,将使用大量的内存。
将表面用于通常为静态(非动画)的显示对象实例。可以拖动或移动实例,但实例内容不应为动画或者有太多的变化。
如果将表面和矢量数据混在一起,则会增加 Flash Player(有时还有计算机)需要处理的工作量。尽可能将表面归为一组。
启用位图缓存
要为显示对象启用位图缓存,请将它的 cacheAsBitmap 属性设置为 true:
mySprite.cacheAsBitmap = true;
将 cacheAsBitmap 属性设置为 true 后,您可能会注意到,显示对象的像素会自动与整个坐标对齐。测试 SWF 文件时,您还会注意到,在复杂矢量图像上执行的任何动画的呈现速度都快得多。
即便将 cacheAsBitmap 已设置为 true,如果出现以下一种或多种情况,也不会创建表面(缓存的位图):
位图高度或宽度超过 2880 个像素;位图分配不成功(由于内存不足而出现的错误)。
设置不透明背景颜色
可以为显示对象设置不透明背景。例如,如果 SWF 的背景中包含复杂的矢量图片,则可以将 opaqueBackground 属性设置为指定的颜色(通常与舞台颜色相同)。将颜色指定为一个数字(通常为十六进制的颜色值)。然后将背景视为一个位图,这有助于优化性能。
当将 cacheAsBitmap 设置为 true 并将 opaqueBackground 属性设置为指定的颜色时,opaqueBackground 属性可以使内部位图不透明而加快呈现速度。如果不将 cacheAsBitmap 设置为 true,opaqueBackground 属性将在显示对象的背景中添加一个不透明的矢量正方形形状。它不会自动创建位图。
应用混合模式
混合模式涉及将一个图像(基图像)的颜色与另一个图像(混合图像)的颜色进行组合来生成第三个图像,结果图像是实际在屏幕上显示的图像。图像中的每个像素值都与另一个图像的对应像素值一起处理的,以便为结果图像中的相同位置生成像素值。
每个显示对象都有 blendMode 属性,可以将其设置为下列混合模式之一。以下是在 BlendMode 类中定义的常量。此外,还可以使用 String 值(在括号中),这些值是常量的实际值。
BlendMode.ADD ("add"):通常用于创建两个图像之间的动画变亮模糊效果。
BlendMode.ALPHA ("alpha"):通常用于在背景上应用前景的透明度。
BlendMode.DARKEN ("darken"):通常用于重叠类型。
BlendMode.DIFFERENCE ("difference"):通常用于创建更多变动的颜色。
BlendMode.ERASE ("erase"):通常用于使用前景 Alpha 剪掉(擦除)背景的一部分。
BlendMode.HARDLIGHT ("hardlight"):通常用于创建阴影效果。
BlendMode.INVERT ("invert"):用于反转背景。
BlendMode.LAYER ("layer"):用于强制为特定显示对象的预构成创建临时缓冲区。
BlendMode.LIGHTEN ("lighten"):通常用于重叠类型。
BlendMode.MULTIPLY ("multiply"):通常用于创建阴影和深度效果。
BlendMode.NORMAL ("normal"):用于指定混合图像的像素值覆盖基本图像的像素值。
BlendMode.OVERLAY ("overlay"):通常用于创建阴影效果。
BlendMode.SCREEN ("screen"):通常用于创建亮点和镜头眩光。
BlendMode.SUBTRACT ("subtract"):通常用于创建两个图像之间的动画变暗模糊效果。
调整 DisplayObject 颜色
可以使用 ColorTransform 类的方法 (flash.geom.ColorTransform) 来调整显示对象的颜色。每个显示对象都有 transform 属性(它是 Transform 类的实例),还包含有关应用到显示对象的各种变形的信息。除了有关几何变形的信息之外,Transform 类还包括 colorTransform 属性,它是 ColorTransform 类的实例,并提供访问来对显示对象进行颜色调整。要访问显示对象的颜色转换信息,可以使用如下代码:
var colorInfo:ColorTransform = myDisplayObject.transform.colorTransform;
创建 ColorTransform 实例后,可以通过读取其属性值来查明已应用了哪些颜色转换,也可以通过设置这些值来更改显示对象的颜色。要在进行任何更改后更新显示对象,必须将 ColorTransform 实例重新分配给 transform.colorTransform 属性。
使用代码设置颜色值
ColorTransform 类的 color 属性可用于为显示对象分配具体的红、绿、蓝 (RGB) 颜色值。请注意,使用 color 属性更改显示对象的颜色时,将会完全更改整个对象的颜色,无论该对象以前是否有多种颜色。
使用代码更改颜色和亮度效果
假设显示对象有多种颜色(例如,数码照片),但是您不想完全重新调整对象的颜色,只想根据现有颜色来调整显示对象的颜色。这种情况下,ColorTransform 类包括一组可用于进行此类调整的乘数属性和偏移属性。乘数属性的名分别为 redMultiplier、greenMultiplier、blueMultiplier 和 alphaMultiplier,它们的作用像彩色照片滤镜(或彩色太阳镜)一样,可以增强或削弱显示对象上的某些颜色。偏移属性(redOffset、greenOffset、blueOffset 和 alphaOffset)可用于额外增加对象上某种颜色的值,或用于指定特定颜色可以具有的最小值。
在"属性"检查器上的"颜色"弹出菜单中选择"高级"时,这些乘数和偏移属性与 Flash 创作工具中影片剪辑元件可用的高级颜色设置相同。
旋转对象
使用 rotation 属性可以旋转显示对象。可以通过读取此值来了解是否旋转了某个对象,如果要旋转该对象,可以将此属性设置为一个数字(以度为单位),表示要应用于该对象的旋转量。
或者,还可以使用转换矩阵来旋转显示对象。
淡化对象
可以通过控制显示对象的透明度来使显示对象部分透明(或完全透明),也可以通过更改透明度来使对象淡入或淡出。DisplayObject 类的 alpha 属性用于定义显示对象的透明度(更确切地说是不透明度)。可以将 alpha 属性设置为介于 0 和 1 之间的任何值,其中 0 表示完全透明,1 表示完全不透明。
还可以使用通过 ColorTransform 类提供的颜色调整来更改显示对象的透明度。
遮罩显示对象
可以通过将一个显示对象用作遮罩来创建一个孔洞,透过该孔洞使另一个显示对象的内容可见。
定义遮罩
要指明一个显示对象将是另一个显示对象的遮罩,请将遮罩对象设置为被遮罩的显示对象的 mask 属性。
被遮罩的显示对象显示在用作遮罩的显示对象的全部不透明区域之内。
用作遮罩的显示对象可拖动、设置动画,并可动态调整大小,可以在单个遮罩内使用单独的形状。遮罩显示对象不必一定需要添加到显示列表中。但是,如果希望在缩放舞台时也缩放遮罩对象,或者如果希望支持用户与遮罩对象的交互(如用户控制的拖动和调整大小),则必须将遮罩对象添加到显示列表中。遮罩对象已添加到显示列表时,显示对象的实际 z 索引(从前到后顺序)并不重要。(除了显示为遮罩对象外,遮罩对象将不会出现在屏幕上。)如果遮罩对象是包含多个帧的一个 MovieClip 实例,则遮罩对象会沿其时间轴播放所有帧,如果没有用作遮罩对象,也会出现同样的情况。通过将 mask 属性设置为 null 可以删除遮罩。
不能使用一个遮罩对象来遮罩另一个遮罩对象。不能设置遮罩显示对象的 alpha 属性。只有填充可用于作为遮罩的显示对象中;笔触都会被忽略。
关于遮蔽设备字体
您可以使用显示对象遮罩用设备字体设置的文本。当使用显示对象遮罩用设备字体设置的文本时,遮罩的矩形边框会用作遮罩形状。也就是说,如果为设备字体文本创建了非矩形的显示对象遮罩,则 SWF 文件中显示的遮罩将是遮罩的矩形边框的形状,而不是遮罩本身的形状。
Alpha 通道遮罩
如果遮罩显示对象和被遮罩的显示对象都使用位图缓存,则支持 Alpha 通道遮罩。
对象动画
动画是使内容移动或者使内容随时间发生变化的过程。脚本动画是视频游戏的基础部分,通常用于将优美、有用的交互线索添加到其它应用程序中。
脚本动画的基本概念是变化一定要发生,而且变化一定要分时间逐步完成。使用常见的循环语句,可很容易在 ActionScript 中使内容重复。但是,在更新显示之前,循环将遍历其所有迭代。要创建脚本动画,需要编写 ActionScript,它随时间重复执行某个动作,每次运行时还更新屏幕。
例如,假设要创建一个简单的动画,如使球沿着屏幕运动。ActionScript 包括一个允许您跟踪时间和相应更新屏幕的简单机制,这意味着您可以编写代码,每次让球移动一点点,直到球到达目标为止。每次移动后,屏幕都会更新,从而使跨舞台的运动在查看器中可见。
从实际观点来看,让脚本动画与 SWF 文件的帧速率同步(换句话说,每次新帧显示时都设计一个动画变化)才有意义,因为这是 Flash Player 更新屏幕的速度。每个显示对象都有 enterFrame 事件,它根据 SWF 文件的帧速率来调度,即每帧一个事件。创建脚本动画的大多数开发人员都使用 enterFrame 事件作为一种方法来创建随时间重复的动作。可以编写代码以侦听 enterFrame 事件,每一帧都让动画球移动一定的量,当屏幕更新时(每一帧),将会在新位置重新绘制该球,从而产生了运动。
另一种随时间重复执行某个动作的方法是使用 Timer 类。每次过了指定的时间时,Timer 实例都会触发事件通知。可以编写通过处理 Timer 类的 timer 事件来执行动画的代码,将时间间隔设置为一个很小的间隔(几分之几秒)。
动态加载显示内容
可以将下列任何外部显示资源加载到 ActionScript 3.0 应用程序中:
在 ActionScript 3.0 中创作的 SWF 文件 ─ 此文件可以是 Sprite、MovieClip 或扩展 Sprite 的任何类。
图像文件 ─ 包括 JPG、PNG 和 GIF 文件。
AVM1 SWF 文件 ─ 在 ActionScript 1.0 或 2.0 中编写的 SWF 文件。
使用 Loader 类可以加载这些资源。
加载显示对象
Loader 对象用于将 SWF 文件和图形文件加载到应用程序中。Loader 类是 DisplayObjectContainer 类的子类。Loader 对象在其显示列表中只能包含一个子显示对象,该显示对象表示它加载的 SWF 或图形文件。加载 SWF 文件或图像后,即可将加载的显示对象移到另一个显示对象容器中。
监视加载进度
文件开始加载后,就创建了 LoaderInfo 对象。LoaderInfo 对象用于提供加载进度、加载者和被加载者的 URL、媒体的字节总数及媒体的标称高度和宽度等信息。LoaderInfo 对象还调度用于监视加载进度的事件。
下图说明了 LoaderInfo 对象的不同用途 ─ 用于 SWF 文件的主类的实例、用于 Loader 对象以及用于由 Loader 对象加载的对象:

可以将 LoaderInfo 对象作为 Loader 对象和加载的显示对象的属性进行访问。加载一开始,就可以通过 Loader 对象的 contentLoaderInfo 属性访问 LoaderInfo 对象。显示对象完成加载后,也可以将 LoaderInfo 对象作为加载的显示对象的属性通过显示对象的 loaderInfo 属性进行访问。已加载显示对象的 loaderInfo 属性是指与 Loader 对象的 contentLoaderInfo 属性相同的 LoaderInfo 对象。换句话说,LoaderInfo 对象是加载的对象与加载它的 Loader 对象之间(加载者和被加载者之间)的共享对象。
指定加载上下文
通过 Loader 类的 load() 或 loadBytes() 方法将外部文件加载到 Flash Player 中时,可以选择性地指定 context 参数。此参数是一个 LoaderContext 对象。
LoaderContext 类包括三个属性,用于定义如何使用加载的内容的上下文:
checkPolicyFile:仅当加载图像文件(不是 SWF 文件)时才会使用此属性。如果将此属性设置为 true,Loader 将检查跨域策略文件的原始服务器。只有内容的来源域不是包含 Loader 对象的 SWF 文件所在的域时才需要此属性。如果服务器授予 Loader 域权限,Loader 域中 SWF 文件的 ActionScript 就可以访问加载图像中的数据;换句话说,可以使用 BitmapData.draw() 命令访问加载的图像中的数据。
请注意,来自 Loader 对象所在域以外的其它域的 SWF 文件可以通过调用 Security.allowDomain() 来允许特定的域。
securityDomain:仅当加载 SWF 文件(不是图像)时才会使用此属性。如果 SWF 文件所在的域与包含 Loader 对象的文件所在的域不同,则指定此属性。指定此选项时,Flash Player 将检查跨域策略文件是否存在,如果存在,来自跨策略文件中允许的域的 SWF 文件可以对加载的 SWF 内容执行跨脚本操作。可以将 flash.system.SecurityDomain.currentDomain 指定为此参数。
applicationDomain:仅当加载使用 ActionScript 3.0 编写的 SWF 文件(不是图像或使用 ActionScript 1.0 或 2.0 编写的 SWF 文件)时才会使用此属性。加载文件时,通过将 applicationDomain 参数设置为 flash.system.ApplicationDomain.currentDomain,可以指定将该文件包括在与 Loader 对象相同的应用程序域中。通过将加载的 SWF 文件放在同一个应用程序域中,可以直接访问它的类。如果要加载的 SWF 文件中包含嵌入的媒体,这会很有帮助,您可以通过其关联的类名访问嵌入的媒体。
处理几何结构
flash.geom 包中包含用于定义几何对象(如,点、矩形和转换矩阵)的类。
几何学基础知识
处理几何学简介
flash.geom 包中包含用于定义几何对象(如,点、矩形和转换矩阵)的类。这些类本身并不一定提供功能,但它们用于定义在其它类中使用的对象的属性。
所有几何类都基于以下概念:将屏幕上的位置表示为二维平面。可以将屏幕看作是具有水平 (x) 轴和垂直 (y) 轴的平面图形。屏幕上的任何位置(或"点")可以表示为 x 和 y 值对,即该位置的"坐标"。
每个显示对象(包括舞台)具有其自己的"坐标空间";实质上,这是其用于标绘子显示对象、图画等位置的图形。通常,"原点"(x 和 y 轴相交的位置,其坐标为 0, 0)位于显示对象的左上角。尽管这始终适用于舞台,但并不一定适用于任何其它显示对象。正如在标准二维坐标系中一样,x 轴上的值越往右越大,越往左越小;对于原点左侧的位置,x 坐标为负值。但是,与传统的坐标系相反,在 ActionScript 中,屏幕 y 轴上的值越往下越大,越往上越小(原点上面的 y 坐标为负值)。由于舞台左上角是其坐标空间的原点,因此,舞台上的任何对象的 x 坐标大于 0 并小于舞台宽度,y 坐标大于 0 并小于舞台高度。
可以使用 Point 类实例来表示坐标空间中的各个点。您可以创建一个 Rectangle 实例来表示坐标空间中的矩形区域。对于高级用户,可以使用 Matrix 实例将多个或复杂变形应用于显示对象。通过使用显示对象的属性,可以将很多简单变形(如旋转、位置以及缩放变化)直接应用于该对象。
重要概念和术语
笛卡尔坐标 (Cartesian coordinate):通常,坐标采用一对数字的形式。两个数字分别是 x 坐标和 y 坐标。
坐标空间 (Coordinate space):显示对象中包含的坐标(其子元素所在的位置)的图形。
原点 (Origin):坐标空间中的一个点,x 轴和 y 轴在此位置相交。该点的坐标为 0, 0。
点 (Point):坐标空间中的一个位置。在 ActionScript 使用的二维坐标系中,点是按其 x 轴和 y 轴位置(点坐标)来定义的。
注册点 (Registration point):显示对象的坐标空间的原点(0, 0 坐标)。
缩放 (Scale):相对于原始大小的对象大小。用作动词时,对象缩放是指伸展或缩小对象以更改其大小。
平移 (Translate):将点的坐标从一个坐标空间更改为另一个坐标空间。
变形 (Transformation):对图形的可视特性进行的调整,如旋转对象、改变其缩放比例、倾斜或扭曲其形状或者改变其颜色。
X 轴 (X axis):ActionScript 使用的二维坐标系中的水平轴。
Y 轴 (Y axis):ActionScript 使用的二维坐标系中的垂直轴。
使用 Point 对象
Point 对象定义一对笛卡尔坐标。它表示二维坐标系中的某个位置。其中 x 表示水平轴,y 表示垂直轴。
要定义 Point 对象,请设置它的 x 和 y 属性。
确定两点之间的距离
可以使用 Point 类的 distance() 方法确定坐标空间两点之间的距离。
平移坐标空间
如果两个显示对象位于不同的显示对象容器中,则它们可能位于不同的坐标空间。您可以使用 DisplayObject 类的 localToGlobal() 方法将坐标平移到舞台中相同(全局)坐标空间。
按指定的角度和距离移动显示对象
您可以使用 Point 类的 polar() 方法将显示对象按特定角度移动特定距离。
Point 类的其它用法
您可以将 Point 对象用于以下方法和属性:

方法或属性
说明
DisplayObjectContainer
areInaccessibleObjectsUnderPoint() getObjectsUnderPoint()
用于返回显示对象容器中某个点下的对象的列表。
BitmapData
hitTest()
用于定义 BitmapData 对象中的像素以及要检查点击的点。
BitmapData
applyFilter() copyChannel() merge()
paletteMap() pixelDissolve() threshold()
用于定义那些定义操作的矩形的位置。
Matrix
deltaTransformPoint() transformPoint()
用于定义您要对其应用变形的点。
Rectangle
bottomRight size topLeft
用于定义这些属性。
使用 Rectangle 对象
Rectangle 对象定义一个矩形区域。Rectangle 对象有一个位置,该位置由其左上角的 x 和 y 坐标以及 width 属性和 height 属性定义。通过调用 Rectangle() 构造函数可以定义新 Rectangle 对象的这些属性。
调整 Rectangle 对象的大小和进行重新定位
您可以通过更改 Rectangle 对象的 x 和 y 属性直接重新定位该对象。这对 Rectangle 对象的宽度或高度没有任何影响。
如果更改 Rectangle 对象的 left 或 top 属性,也可以重新定位,并且该对象的 x 和 y 属性分别与 left 和 top 属性匹配。但是,Rectangle 对象的左下角位置不发生更改,所以调整了对象的大小。
如果更改 Rectangle 对象的 bottom 或 right 属性,该对象的左上角位置不发生更改,所以相应地调整了对象的大小。
可以使用 offset() 方法重新定位 Rectangle 对象,offsetPt() 方法工作方式类似,只不过它是将 Point 对象作为参数,而不是将 x 和 y 偏移量值作为参数。
还可以使用 inflate() 方法调整 Rectangle 对象的大小,该方法包含两个参数,dx 和 dy。dx 参数表示矩形的左边和右边距中心的像素数,而 dy 参数表示矩形的顶边和底边距中心的像素数。
inflatePt() 方法作方式类似,只不过它是将 Point 对象作为参数,而不是将 dx 和 dy 的值作为参数。
确定 Rectangle 对象的联合和交集
可以使用 union() 方法来确定由两个矩形的边界形成的矩形区域。
可以使用 intersection() 方法来确定由两个矩形重叠区域形成的矩形区域。
使用 intersects() 方法查明两个矩形是否相交。也可以使用 intersects() 方法查明显示对象是否在舞台的某个区域中。可以使用 intersects() 方法查明两个显示对象的边界矩形是否重叠。可以使用 DisplayObject 类的 getRect() 方法来包括显示对象笔触可添加到边界区域中的其它任何空间。
Rectangle 对象的其它用法
Rectangle 对象可用于以下方法和属性:

方法或属性
描述
BitmapData
applyFilter()、colorTransform()、copyChannel()、copyPixels()、draw()、fillRect()、generateFilterRect()、getColorBoundsRect()、getPixels()、merge()、paletteMap()、pixelDissolve()、setPixels() 和 threshold()
用作某些参数的类型以定义 BitmapData 对象的区域。
DisplayObject
getBounds()、getRect()、scrollRect、scale9Grid
用作属性的数据类型或返回的数据类型。
PrintJob
addPage()
用于定义 printArea 参数。
Sprite
startDrag()
用于定义 bounds 参数。
TextField
getCharBoundaries()
用作返回值类型。
Transform
pixelBounds
用作数据类型。


使用 Matrix 对象
Matrix 类表示一个转换矩阵,它确定如何将点从一个坐标空间映射到另一个坐标空间。可以对显示对象执行不同的图形转换,方法是设置 Matrix 对象的属性,将该 Matrix 对象应用于 Transform 对象的 matrix 属性,然后应用该 Transform 对象作为显示对象的 transform 属性。这些转换函数包括平移(x 和 y 重新定位)、旋转、缩放和倾斜。
定义 Matrix 对象
虽然可以通过直接调整 Matrix 对象的属性(a、b、c、d、tx 和 ty)来定义矩阵,但更简单的方法是使用 createBox() 方法。使用此方法提供的参数可以直接定义生成的矩阵的缩放、旋转和平移效果。
还可以使用 scale()、rotate() 和 translate() 方法调整 Matrix 对象的缩放、旋转和平移效果。请注意,这些方法合并了现有 Matrix 对象的值。
要将倾斜转换应用到 Matrix 对象,请调整该对象的 b 或 c 属性。调整 b 属性将矩阵垂直倾斜,并调整 c 属性将矩阵水平倾斜。
可以将矩阵转换应用到显示对象的 transform 属性。
使用绘图 API
绘图 API 使用基础知识
使用绘图 API 简介
绘图 API 是 ActionScript 中的一项内置功能的名称,您可以使用该功能来创建矢量图形(直线、曲线、形状、填充和渐变),并使用 ActionScript 在屏幕上显示它们。flash.display.Graphics 类提供了这一功能。您可以在任何 Shape、Sprite 或 MovieClip 实例中使用 ActionScript 进行绘制(使用其中的每个类中定义的 graphics 属性)。(实际上,每个类的 graphics 属性都是 Graphics 类的实例。)
重要概念和术语
以下参考列表包含将会在本章中遇到的重要术语:
锚点 (Anchor point):二次贝塞尔曲线的两个端点之一。
控制点 (Control point):该点定义了二次贝塞尔曲线的弯曲方向和弯曲量。弯曲的线绝不会到达控制点;但是,曲线就好像朝着控制点方向进行绘制的。
坐标空间 (Coordinate space):显示对象中包含的坐标(其子元素所在的位置)的图形。
填充 (Fill):形状内的实心部分,它包含一条用颜色填充的线条,或者整个形状都没有轮廓。
渐变 (Gradient):此颜色是指从一种颜色逐渐过渡到一种或多种其它颜色(相对于纯色而言)。
点 (Point):坐标空间中的一个位置。在 ActionScript 使用的二维坐标系中,点是按其 x 轴和 y 轴位置(点坐标)来定义的。
二次贝塞尔曲线 (Quadratic Bezier curve):一种由特定数学公式定义的曲线类型。在这种类型的曲线中,曲线形状是根据锚点(曲线端点)和控制点(定义曲线的弯曲方向和弯曲量)的位置计算的。
缩放 (Scale):相对于原始大小的对象大小。用作动词时,对象缩放是指伸展或缩小对象以更改其大小。
笔触 (Stroke):形状的轮廓部分,它包含一条用颜色填充的线条,或未填充的形状的多个线条。
平移 (Translate):将点的坐标从一个坐标空间更改为另一个坐标空间。
X 轴 (X axis):ActionScript 使用的二维坐标系中的水平轴。
Y 轴 (Y axis):ActionScript 使用的二维坐标系中的垂直轴。
了解 Graphics 类
每个 Shape、Sprite 和 MovieClip 对象都具有一个 graphics 属性,它是 Graphics 类的一个实例。Graphics 类包含用于绘制线条、填充和形状的属性和方法。如果要将显示对象仅用作内容绘制画布,则可以使用 Shape 实例。Shape 实例的性能优于其它用于绘制的显示对象,因为它不会产生 Sprite 和 MovieClip 类中的附加功能的开销。如果希望能够在显示对象上绘制图形内容,并且还希望该对象包含其它显示对象,则可以使用 Sprite 实例。
绘制直线和曲线
使用 Graphics 实例进行的所有绘制均基于包含直线和曲线的基本绘制。因此,必须使用一系列相同的步骤来执行所有 ActionScript 绘制:定义线条和填充样式、 设置初始绘制位置、 绘制直线、曲线和形状(可选择移动绘制点)、 如有必要,完成创建填充 。
定义线条和填充样式
要使用 Shape、Sprite 或 MovieClip 实例的 graphics 属性进行绘制,您必须先定义在绘制时使用的样式(线条大小和颜色、填充颜色)。就像使用 Adobe Flash CS3 Professional 或其它绘图应用程序中的绘制工具一样,使用 ActionScript 进行绘制时,可以使用笔触进行绘制,也可以不使用笔触;可以使用填充颜色进行绘制,也可以不使用填充颜色。您可以使用 lineStyle() 或 lineGradientStyle() 方法来指定笔触的外观。要创建纯色线条,请使用 lineStyle() 方法。调用此方法时,您指定的最常用的值是前三个参数:线条粗细、颜色以及 Alpha。Alpha 参数的默认值为 1.0 (100%),因此,如果需要完全不透明的线条,可以将该参数的值保持不变。lineStyle() 方法还接受两个用于像素提示和缩放模式的额外参数。
要创建渐变线条,请使用 lineGradientStyle() 方法。
如果要创建填充形状,请在开始绘制之前调用 beginFill()、beginGradientFill() 或 beginBitmapFill() 方法。其中的最基本方法 beginFill() 接受以下两个参数:填充颜色以及填充颜色的 Alpha 值(可选)。
调用任何填充方法时,将隐式地结束任何以前的填充,然后再开始新的填充。调用任何指定笔触样式的方法时,将替换以前的笔触,但不会改变以前指定的填充,反之亦然。
指定了线条样式和填充属性后,下一步是指示绘制的起始点。Graphics 实例具有一个绘制点,就像在一张纸上的钢笔尖一样。无论绘制点位于什么位置,它都是开始执行下一个绘制动作的位置。最初,Graphics 对象将它绘制时所在对象的坐标空间中的点 (0,0) 作为起始绘制点。要在其它点开始进行绘制,您可以先调用 moveTo() 方法,然后再调用绘制方法之一。这类似于将钢笔尖从纸上抬起,然后将其移到新位置。
确定绘制点后,可通过使用对绘制方法 lineTo()(用于绘制直线)和 curveTo()(用于绘制曲线)的一系列调用来进行绘制。在进行绘制时,可随时调用 moveTo() 方法,将绘制点移到新位置而不进行绘制。
在进行绘制时,如果已指定了填充颜色,可以指示 Adobe Flash Player 调用 endFill() 方法来结束填充。如果绘制的形状没有闭合(换句话说,在调用 endFill() 时,绘制点不在形状的起始点),调用 endFill() 方法时,Flash Player 将自动绘制一条直线以闭合形状,该直线从当前绘制点到最近一次 moveTo() 调用中指定的位置。如果已开始填充并且没有调用 endFill(),调用 beginFill()(或其它填充方法之一)时,将关闭当前填充并开始新的填充。
绘制直线
调用 lineTo() 方法时,Graphics 对象将绘制一条直线,该直线从当前绘制点到指定为方法调用中的两个参数的坐标,以便使用指定的线条样式进行绘制。
绘制曲线
curveTo() 方法可以绘制二次贝塞尔曲线。这将绘制一个连接两个点(称为锚点)的弧,同时向第三个点(称为控制点)弯曲。Graphics 对象使用当前绘制位置作为第一个锚点。调用 curveTo() 方法时,将传递以下四个参数:控制点的 x 和 y 坐标,后跟第二个锚点的 x 和 y 坐标。
使用内置方法绘制形状
为了便于绘制常见形状(如圆、椭圆、矩形以及带圆角的矩形),ActionScript 3.0 中提供了用于绘制这些常见形状的方法。它们是 Graphics 类的 drawCircle()、drawEllipse()、drawRect()、drawRoundRect() 和 drawRoundRectComplex() 方法。这些方法可用于替代 lineTo() 和 curveTo() 方法。但要注意,在调用这些方法之前,您仍需指定线条和填充样式。
在 Sprite 或 MovieClip 对象中,使用 graphics 属性创建的绘制内容始终出现在该对象包含的所有子级显示对象的后面。另外,graphics 属性内容不是单独的显示对象,因此,它不会出现在 Sprite 或 MovieClip 对象的子级列表中。
创建渐变线条和填充
graphics 对象也可以绘制渐变笔触和填充,而不是纯色笔触和填充。渐变笔触是使用 lineGradientStyle() 方法创建的;渐变填充是使用 beginGradientFill() 方法创建的。
这两种方法接受相同的参数。前四个参数是必需的,即类型、颜色、Alpha 以及比率。其余四个参数是可选的,但对于高级自定义非常有用。
第一个参数指定要创建的渐变类型。可接受的值是 GradientFill.LINEAR 或 GradientFill.RADIAL。
第二个参数指定要使用的颜色值的数组。在线性渐变中,将从左向右排列颜色。在放射状渐变中,将从内到外排列颜色。数组颜色的顺序表示在渐变中绘制颜色的顺序。
第三个参数指定前一个参数中相应颜色的 Alpha 透明度值。
第四个参数指定比率或每种颜色在渐变中的重要程度。可接受的值范围是 0-255。这些值并不表示任何宽度或高度,而是表示在渐变中的位置;0 表示渐变开始,255 表示渐变结束。比率数组必须按顺序增加,并且包含的条目数与第二个和第三个参数中指定的颜色和 Alpha 数组相同。
虽然第五个参数(转换矩阵)是可选的,但通常会使用该参数,因为它提供了一种简便且有效的方法来控制渐变外观。此参数接受 Matrix 实例。为渐变创建 Matrix 对象的最简单方法是使用 Matrix 类的 createGradientBox() 方法。
定义 Matrix 对象以用于渐变
可以使用 flash.display.Graphics 类的 beginGradientFill() 和 lineGradientStyle() 方法来定义在形状中使用的渐变。定义渐变时,需要提供一个矩阵作为这些方法的其中一个参数。
定义矩阵的最简单方法是使用 Matrix 类的 createGradientBox() 方法,该方法创建一个用于定义渐变的矩阵。可以使用传递给 createGradientBox() 方法的参数来定义渐变的缩放、旋转和位置。createGradientBox() 方法接受以下参数:
渐变框宽度:渐变扩展到的宽度(以像素为单位)
渐变框高度:渐变扩展到的高度(以像素为单位)
渐变框旋转:将应用于渐变的旋转角度(以弧度为单位)
水平平移:将渐变水平移动的距离(以像素为单位)
垂直平移:将渐变垂直移动的距离(以像素为单位)
请注意,渐变填充的宽度和高度是由渐变矩阵的宽度和高度决定的,而不是由使用 Graphics 对象绘制的宽度和高度决定的。使用 Graphics 对象进行绘制时,您绘制的内容位于渐变矩阵中的这些坐标处。即使使用 Graphics 对象的形状方法之一(如 drawRect()),渐变也不会将其自身伸展到绘制的形状的大小;必须在渐变矩阵本身中指定渐变的大小。
lineGradientStyle() 方法的工作方式与 beginGradientFill() 类似,所不同的是,除了定义渐变外,您还必须在绘制之前使用 lineStyle() 方法指定笔触粗细。
将 Math 类与绘制方法配合使用
Graphics 对象可以绘制圆和正方形,但也可以绘制更复杂的形状,尤其是在将绘制方法与 Math 类的属性和方法配合使用时。Math 类包含人们通常很感兴趣的数学常量,如 Math.PI(约等于 3.14159265...),此常量表示圆的周长与其直径的比率。它还包含三角函数的方法,其中包括 Math.sin()、Math.cos() 和 Math.tan() 等。使用这些方法和常量绘制形状可产生更动态的视觉效果,尤其是用于重复或递归时。
Math 类的很多方法都要求以弧度为单位来测量圆弧,而不是使用角度。
使用绘图 API 进行动画处理
使用绘图 API 创建内容的一个优点是,您并不限于将内容放置一次。可通过保留和修改用于绘制的变量来修改所绘制的内容。您可以通过更改变量和重绘(在一段帧上或使用计时器)来利用原有的动画。
过滤显示对象
ActionScript 3.0 包括 flash.filters 包,它包含一系列位图效果滤镜类,允许开发人员以编程方式对位图应用滤镜并显示对象,以达到图形处理应用程序中所具有的许多相同效果。
过滤显示对象的基础知识
过滤显示对象简介
ActionScript 3.0 包括九种可应用于任何显示对象或 BitmapData 实例的滤镜。滤镜的范围从基本滤镜(如投影和发光滤镜)到用于创建各种效果的复杂滤镜(如置换图滤镜和卷积滤镜)。
重要概念和术语
斜角:通过使两侧的像素变亮并使相对两侧的像素变暗所形成的一个边缘,它可以产生常用于凸起或凹进按钮和类似图形的三维边界效果。
卷积:通过使用各种比率将每个像素的值与其周围某些像素或全部像素的值合并以使图像中的像素发生扭曲。
置换:将图像中的像素移动到新位置。
矩阵:用于执行特定数学计算的网格数字,使用方法是对网格中的数字应用不同的值,然后合并结果。
建和应用滤镜
使用滤镜可以对位图和显示对象应用从投影到斜角和模糊等各种效果。由于将每个滤镜定义为一个类,因此应用滤镜涉及创建滤镜对象的实例,这与构造任何其它对象并没有区别。创建了滤镜对象的实例后,通过使用该对象的 filters 属性可以很容易地将此实例应用于显示对象;如果是 BitmapData 对象,可以使用 applyFilter() 方法。
创建新滤镜
若要创建新滤镜对象,只需调用所选的滤镜类的构造函数方法即可。
应用滤镜
构造滤镜对象后,可以将其应用于显示对象或 BitmapData 对象;应用滤镜的方式取决于您应用该滤镜的对象。
对显示对象应用滤镜
对显示对象应用滤镜效果时,可以通过 filters 属性应用这些效果。显示对象的 filters 属性是一个 Array 实例,其中的元素是应用于该显示对象的滤镜对象。若要对显示对象应用单个滤镜,请创建该滤镜实例,将其添加到 Array 实例,再将该 Array 对象分配给显示对象的 filters 属性。
如果要为该对象分配多个滤镜,只需在将 Array 实例分配给 filters 属性之前将所有滤镜添加到该实例即可。可以通过将多个对象作为参数传递给 Array 的构造函数,将多个对象添加到 Array。
在创建包含滤镜的数组时,您可以使用 new Array() 构造函数创建该数组(如前面示例所示),也可以使用 Array 文本语法将滤镜括在方括号 ([]) 中。
如果对显示对象应用多个滤镜,则会按顺序以累积方式应用这些滤镜。例如,如果滤镜数组有两个元素:先添加的斜角滤镜和后添加的投影滤镜,则投影滤镜既会应用于斜角滤镜,也会应用于显示对象。这是由于投影滤镜在滤镜数组中处于第二的位置。如果想要以非累积方式应用滤镜,则必须对显示对象的新副本应用每个滤镜。
删除显示对象中的滤镜
删除显示对象中的所有滤镜非常简单,只需为 filters 属性分配一个 null 值即可。
对 BitmapData 对象应用滤镜
对 BitmapData 对象应用滤镜需要使用 BitmapData 对象的 applyFilter() 方法。此方法不会修改原始的源图像;而是将对源图像应用滤镜的结果存储在调用 applyFilter() 方法的 BitmapData 实例中。
滤镜的工作原理
显示对象过滤是通过将原始对象的副本缓存为透明位图来工作的。
将滤镜应用于显示对象后,只要此对象具有有效的滤镜列表,Adobe Flash Player 就会将该对象缓存为位图。然后,将此位图用作所有后续应用的滤镜效果的原始图像。
每个显示对象通常包含两个位图:一个包含原始未过滤的源显示对象,另一个用于过滤后的最终图像。呈现时使用最终图像。只要显示对象不发生更改,最终图像就不需要更新。
使用滤镜的潜在问题
滤镜和位图缓存
若要对显示对象应用滤镜,必须启用该对象的位图缓存。在对 cacheAsBitmap 属性设置为 false 的显示对象应用滤镜时,Flash Player 会自动将该对象的 cacheAsBitmap 属性的值设置为 true。如果您以后删除了该显示对象中的所有滤镜,Flash Player 会将 cacheAsBitmap 属性重置为最后设置的值。
在运行时更改滤镜
如果已经对显示对象应用了一个或多个滤镜,则无法向 filters 属性数组添加其它滤镜。若要添加或更改应用的这组滤镜,需要创建整个滤镜数组的副本,然后对此(临时)数组进行修改。然后,将此数组重新分配给显示对象的 filters 属性,这样才能将滤镜应用于该对象。
滤镜和对象变形
在显示对象的边框矩形之外的任何过滤区域(例如投影)都不能视为可进行点击检测(确定实例是否与其它实例重叠或交叉)的表面。由于 DisplayObject 类的点击检测方法是基于矢量的,因此无法在位图结果上执行点击检测。例如,如果您对按钮实例应用斜角滤镜,则在该实例的斜角部分,点击检测不可用。
滤镜不支持缩放、旋转和倾斜;如果过滤的显示对象本身进行了缩放(如果 scaleX 和 scaleY 不是 100%),则滤镜效果将不随该实例缩放。这意味着,实例的原始形状将旋转、缩放或倾斜;而滤镜不随实例一起旋转、缩放或倾斜。
可以使用滤镜给实例添加动画,以形成理想的效果,或者嵌套实例并使用 BitmapData 类使滤镜动起来,以获得此效果。
滤镜和位图对象
对 BitmapData 对象应用滤镜时,cacheAsBitmap 属性会自动设置为 true。通过这种方式,滤镜实际上是应用于对象的副本而不是原始对象。
之后,会将此副本放在主显示(原始对象)上,尽量接近最近的像素。如果原始位图的边框发生更改,则会从头重新创建过滤的副本位图,而不进行伸展或扭曲。
如果清除了显示对象的所有滤镜,cacheAsBitmap 属性会重置为应用滤镜之前的值。
可用的显示滤镜
ActionScript 3.0 包括 9 个可用于显示对象和 BitmapData 对象的滤镜类:
斜角滤镜(BevelFilter 类)
模糊滤镜(BlurFilter 类)
投影滤镜(DropShadowFilter 类)
发光滤镜(GlowFilter 类)
渐变斜角滤镜(GradientBevelFilter 类)
渐变发光滤镜(GradientGlowFilter 类)
颜色矩阵滤镜(ColorMatrixFilter 类)
卷积滤镜(ConvolutionFilter 类)
置换图滤镜(DisplacementMapFilter 类)
前六个滤镜是简单滤镜,可用于创建一种特定效果,并可以对效果进行某种程度的自定义。可以使用 ActionScript 应用这六个滤镜,也可以在 Adobe Flash CS3 Professional 中使用"滤镜"面板将其应用于对象。因此,即使您要使用 ActionScript 应用滤镜,如果有 Flash 创作工具,也可以使用可视界面快速尝试不同的滤镜和设置,弄清楚如何创建需要的效果。
最后三个滤镜仅在 ActionScript 中可用。这些滤镜(颜色矩阵滤镜、卷积滤镜和置换图滤镜)所创建的效果具有极其灵活的形式;它们不仅仅可以进行优化以生成单一的效果,而且还具有强大的功能和灵活性。
不管是简单滤镜还是复杂滤镜,每个滤镜都可以使用其属性进行自定义。通常,您有两种方法用于设置滤镜属性。所有滤镜都允许通过向滤镜对象的构造函数传递参数值来设置属性。或者,不管您是否通过传递参数来设置滤镜属性,都可以在以后通过设置滤镜对象的属性值来调整滤镜。
斜角滤镜
BevelFilter 类允许您对过滤的对象添加三维斜面边缘。此滤镜可使对象的硬角或边缘具有硬角或边缘被凿削或呈斜面的效果。
BevelFilter 类属性允许您自定义斜角的外观。您可以设置加亮和阴影颜色、斜角边缘模糊、斜角角度和斜角边缘的位置,甚至可以创建挖空效果。
模糊滤镜
BlurFilter 类可使显示对象及其内容具有涂抹或模糊的效果。模糊效果可以用于产生对象不在焦点之内的视觉效果,也可以用于模拟快速运动,比如运动模糊。通过将模糊滤镜的 quality 属性设置为低,可以模拟轻轻离开焦点的镜头效果。将 quality 属性设置为高会产生类似高斯模糊的平滑模糊效果。
投影滤镜
投影给人一种目标对象上方有独立光源的印象。可以修改此光源的位置和强度,以产生各种不同的投影效果。
投影滤镜使用与模糊滤镜的算法相似的算法。主要区别是投影滤镜有更多的属性,您可以修改这些属性来模拟不同的光源属性(如 Alpha、颜色、偏移和亮度)。
投影滤镜还允许您对投影的样式应用自定义变形选项,包括内侧或外侧阴影和挖空(也称为剪切块)模式。
发光滤镜
GlowFilter 类对显示对象应用加亮效果,使显示对象看起来像是被下方的灯光照亮,可创造出一种柔和发光效果。
与投影滤镜类似,发光滤镜包括的属性可修改光源的距离、角度和颜色,以产生各种不同效果。GlowFilter 还有多个选项用于修改发光样式,包括内侧或外侧发光和挖空模式。
渐变斜角滤镜
GradientBevelFilter 类允许您对显示对象或 BitmapData 对象应用增强的斜角效果。在斜角上使用渐变颜色可以大大改善斜角的空间深度,使边缘产生一种更逼真的三维外观效果。
渐变发光滤镜
GradientGlowFilter 类允许您对显示对象或 BitmapData 对象应用增强的发光效果。该效果可使您更好地控制发光颜色,因而可产生一种更逼真的发光效果。另外,渐变发光滤镜还允许您对对象的内侧、外侧或上侧边缘应用渐变发光。
颜色矩阵滤镜
ColorMatrixFilter 类用于操作过滤对象的颜色和 Alpha 值。它允许您进行饱和度更改、色相旋转(将调色板从一个颜色范围移动到另一个颜色范围)、将亮度更改为 Alpha,以及生成其它颜色操作效果,方法是使用一个颜色通道中的值,并将这些值潜移默化地应用于其它通道。
从概念上来说,滤镜将逐一处理源图像中的像素,并将每个像素分为红、绿、蓝和 Alpha 组件。然后,用每个值乘以颜色矩阵中提供的值,将结果加在一起以确定该像素将显示在屏幕上的最终颜色值。滤镜的 matrix 属性是一个由 20 个数字组成的数组,用于计算最终颜色。
有关颜色矩阵滤镜的其它信息和示例,请参阅 Adobe 开发人员中心网站上提供的"Using Matrices for Transformations, Color Adjustments, and Convolution Effects in Flash"(在 Flash 中使用矩阵实现变形、颜色调整和卷积效果)文章。
卷积滤镜
ConvolutionFilter 类可用于对 BitmapData 对象或显示对象应用广泛的图像变形,如模糊、边缘检测、锐化、浮雕和斜角。
从概念上来说,卷积滤镜会逐一处理源图像中的每个像素,并使用像素和它周围的像素的值来确定该像素的最终颜色。指定为数值数组的矩阵可以指示每个特定邻近像素的值对最终结果值具有何种程度的影响。
最常用的矩阵类型是 3 x 3 矩阵。此矩阵包括九个值:
N N N
N P N
N N N
Flash Player 对特定像素应用卷积滤镜时,它会考虑像素本身的颜色值(本示例中的"P"),以及周围像素的值(本示例中的"N")。而通过设置矩阵中的值,可以指定特定像素在影响生成的图像方面所具有的优先级。
置换图滤镜
DisplacementMapFilter 类使用 BitmapData 对象(称为置换图图像)中的像素值在新对象上执行置换效果。通常,置换图图像与将要应用滤镜的实际显示对象或 BitmapData 实例不同。置换效果包括置换过滤的图像中的像素,也就是说,将这些像素移开原始位置一定距离。此滤镜可用于产生移位、扭曲或斑点效果。
应用于给定像素的置换位置和置换量由置换图图像的颜色值确定。使用滤镜时,除了指定置换图图像外,还要指定以下值,以便控制置换图图像中计算置换的方式:
映射点:过滤图像上的位置,在该点将应用置换滤镜的左上角。如果只想对图像的一部分应用滤镜,可以使用此值。
X 组件:影响像素的 x 位置的置换图图像的颜色通道。
Y 组件:影响像素的 y 位置的置换图图像的颜色通道。
X 缩放比例:指定 x 轴置换强度的乘数值。
Y 缩放比例:指定 y 轴置换强度的乘数值。
滤镜模式:确定在移开像素后形成的空白区域中,Flash Player 应执行什么操作。在 DisplacementMapFilterMode 类中定义为常量的选项可以显示原始像素(滤镜模式 IGNORE)、从图像的另一侧环绕像素(滤镜模式 WRAP,这是默认设置)、使用最近的移位像素(滤镜模式 CLAMP)或用颜色填充空间(滤镜模式 COLOR)。
处理影片剪辑
MovieClip 类是在 Adobe Flash CS3 Professional 中创建的动画和影片剪辑元件的核心类。它除具有显示对象的所有行为和功能外,还具有用于控制影片剪辑的时间轴的其它属性和方法。
影片剪辑基础知识
影片剪辑处理简介
只要在 Flash 中创建影片剪辑元件,Flash 就会将该元件添加到该 Flash 文档的库中。默认情况下,此元件会成为 MovieClip 类的一个实例,因此具有 MovieClip 类的属性和方法。
在将某个影片剪辑元件的实例放置在舞台上时,如果该影片剪辑具有多个帧,它会自动按其时间轴进行回放,除非使用 ActionScript 更改其回放。此时间轴使 MovieClip 类与其它类区别开来,允许您在 Flash 创作工具中通过补间动画或补间形状来创建动画。相反,对于作为 Sprite 类的实例的显示对象,您只需以编程方式更改该对象的值即可创建动画。
在 ActionScript 的早期版本中,MovieClip 类是舞台上所有实例的基类。在 ActionScript 3.0 中,影片剪辑只是可以在屏幕上显示的众多显示对象中的一个。如果使用显示对象时不需要时间轴,则使用 Shape 类或 Sprite 类替代 MovieClip 类可能会提高呈现性能。
重要概念和术语
AVM1 SWF:使用 ActionScript 1.0 或 ActionScript 2.0 创建的 SWF 文件,通常以 Flash Player 8 或更早版本为目标播放器。
AVM2 SWF:使用 ActionScript 3.0 for Adobe Flash Player 9 创建的 SWF 文件。
外部 SWF:单独从项目 SWF 文件创建的 SWF 文件,将加载到项目 SWF 文件中并在该 SWF 文件中回放。
帧:时间轴上划分时间的最小单位。与运动图像电影胶片一样,每个帧都类似于动画在特定时间的快照,当快速按顺序播放各个帧时,会产生动画的效果。
时间轴:构成影片剪辑动画序列的一系列帧的比喻形式。MovieClip 对象的时间轴等同于 Flash 创作工具中的时间轴。
播放头:一个标记,用于标识在给定时刻在时间轴中所处的位置(帧)。
处理 MovieClip 对象
在发布 SWF 文件时,Flash 会将舞台上的所有影片剪辑元件实例转换为 MovieClip 对象。通过在属性检查器的"实例名称"字段中指定影片剪辑元件的实例名称,您可以在 ActionScript 中使用该元件。在创建 SWF 文件时,Flash 会生成在舞台上创建该 MovieClip 实例的代码并使用该实例名称声明一个变量。如果您已经命名了嵌套在其它已命名影片剪辑内的影片剪辑,则这些子级影片剪辑将被视为父级影片剪辑的属性,您可以使用点语法访问该子级影片剪辑。
控制影片剪辑回放
Flash 利用时间轴来形象地表示动画或状态改变。任何使用时间轴的可视元素都必须为 MovieClip 对象或从 MovieClip 类扩展而来。尽管 ActionScript 可控制任何影片剪辑的停止、播放或转至时间轴上的另一点,但不能用于动态创建时间轴或在特定帧添加内容,这项工作仅能使用 Flash 创作工具来完成。
MovieClip 在播放时将以 SWF 文件的帧速率决定的速度沿着其时间轴推进。或者,您也可以通过在 ActionScript 中设置 Stage.frameRate 属性来覆盖此设置。
播放影片剪辑和停止回放
play() 和 stop() 方法允许对时间轴上的影片剪辑进行基本控制。
快进和后退
也可以使用 nextFrame() 和 prevFrame() 方法手动向前或向后沿时间轴移动播放头。调用这两种方法中的任一方法均会停止回放并分别使播放头向前或向后移动一帧。
在正常回放过程中,如果影片剪辑包含多个帧,播放时将会无限循环播放,也就是说在经过最后一帧后将返回到第 1 帧。使用 prevFrame() 或 nextFrame() 时,不会自动发生此行为(在播放头位于第 1 帧时调用 prevFrame() 不会将播放头移动到最后一帧)。
跳到不同帧和使用帧标签
向新帧发送影片剪辑非常简单。调用 gotoAndPlay() 或 gotoAndStop() 将使影片剪辑跳到指定为参数的帧编号。或者,您可以传递一个与帧标签名称匹配的字符串。可以为时间轴上的任何帧分配一个标签。为此,选择时间轴上的某一帧,然后在属性检查器的"帧标签"字段中输入一个名称。
当创建复杂的影片剪辑时,使用帧标签比使用帧编号具有明显优势。当动画中的帧、图层和补间的数量变得很大时,应考虑给重要的帧加上具有解释性说明的标签来表示影片剪辑中的行为转换(例如,"离开"、"行走"或"跑")。这可提高代码的可读性,同时使代码更加灵活,因为转到指定帧的 ActionScript 调用是指向单一参考("标签"而不是特定帧编号)的指针。如果您以后决定将动画的特定片段移动到不同的帧,则无需更改 ActionScript 代码,只要将这些帧的相同标签保持在新位置即可。
为便于在代码中表示帧标签,ActionScript 3.0 包括了 FrameLabel 类。此类的每个实例均代表一个帧标签,并具有一个 name 属性(表示在属性检查器中指定的帧标签的名称)和一个 frame 属性(表示该标签在时间轴上所处帧的帧编号)。
为了访问与影片剪辑实例相关联的 FrameLabel 实例,MovieClip 类包括了两个可直接返回 FrameLabel 对象的属性。currentLabels 属性返回一个包含影片剪辑整个时间轴上所有 FrameLabel 对象的数组。currentLabel 属性返回一个表示在时间轴上最近遇到的帧标签的 FrameLabel 对象。
处理场景
在 Flash 创作环境中,您可以使用场景来区分 SWF 文件播放时将要经过的一系列时间轴。使用 gotoAndPlay() 或 gotoAndStop() 方法的第二个参数,可以指定要向其发送播放头的场景。所有 FLA 文件开始时都只有初始场景,但您可以创建新的场景。
使用场景并非始终是最佳方法,因为场景有许多缺点。包含多个场景的 Flash 文档可能很难维护,尤其是在存在多个作者的环境中。多个场景也会使带宽效率降低,因为发布过程会将所有场景合并为一个时间轴。这样将使所有场景进行渐进式下载,即使从不会播放这些场景。因此,除非是组织冗长的基于多个时间轴的动画,否则通常不鼓励使用多个场景。
MovieClip 类的 scenes 属性返回表示 SWF 文件中所有场景的 Scene 对象的数组。currentScene 属性返回一个表示当前正在播放的场景的 Scene 对象。
Scene 类具有多个提供有关场景信息的属性。labels 属性返回表示该场景中帧标签的 FrameLabel 对象的数组。name 属性将以字符串形式返回场景名称。numFrames 属性返回一个表示场景中帧的总数的整数。
使用 ActionScript 创建 MovieClip 对象
在 Flash 中向屏幕中添加内容的一个方法是将资源从库中拖放到舞台上,但不是仅有这一种方法。对于复杂项目,经验丰富的开发人员通常更喜欢以编程方式创建影片剪辑。这种方法具有多个优点:代码更易于重用、编译时速度加快,以及仅可在 ActionScript 中进行的更复杂的修改。
ActionScript 3.0 的显示列表 API 简化了动态创建 MovieClip 对象的过程。直接实例化 MovieClip 实例的功能从向显示列表中添加该实例的过程中分离出来,从而更加灵活、简单,而不会牺牲控制性能。
在 ActionScript 3.0 中,当以编程方式创建影片剪辑(或任何其它显示对象)实例时,只有通过对显示对象容器调用 addChild() 或 addChildAt() 方法将该实例添加到显示列表中后,才能在屏幕上看到该实例。这允许您创建影片剪辑、设置其属性,甚至可以在向屏幕呈现该影片剪辑之前调用方法。
加载外部 SWF 文件
在 ActionScript 3.0 中,SWF 文件是使用 Loader 类来加载的。若要加载外部 SWF 文件,ActionScript 需要执行以下 4 个操作:
用文件的 URL 创建一个新的 URLRequest 对象。
创建一个新的 Loader 对象。
调用 Loader 对象的 load() 方法,并以参数形式传递 URLRequest 实例。
对显示对象容器(如 Flash 文档的主时间轴)调用 addChild() 方法,将 Loader 实例添加到显示列表中。
最后,代码如下所示:
var request:URLRequest = new URLRequest("http://www.[yourdomain].com/externalSwf.swf");
var loader:Loader = new Loader()
loader.load(request);
addChild(loader);
通过指定图像文件的 URL 而不是 SWF 文件的 URL,可以使用上述同样的代码加载外部图像文件,如 JPEG、GIF 或 PNG 图像。SWF 文件不同于图像文件,可能包含 ActionScript。因此,虽然加载 SWF 文件的过程可能与加载图像的过程完全相同,但如果您计划使用 ActionScript 以某种方式与外部 SWF 文件通信,则在加载该外部 SWF 文件时,执行加载的 SWF 文件和被加载的 SWF 文件必须位于同一个安全沙箱中。另外,如果外部 SWF 文件包含了与执行加载的 SWF 文件中的类共享同一命名空间的类,可能需要为被加载的 SWF 文件创建新的应用程序域才能避免命名空间冲突。
当成功加载外部 SWF 文件后,可通过 Loader.content 属性访问该文件。如果该外部 SWF 文件是针对 ActionScript 3.0 发布的,则加载的文件将为影片剪辑或 sprite,具体取决于所扩展的类。
处理文本
处理文本的基础知识
处理文本简介
在 Adobe Flash Player 中,若要在屏幕上显示文本,可以使用 TextField 类的实例。TextField 类是 Adobe Flex 框架和 Flash 创作环境中提供的其它基于文本的组件(如 TextArea 组件或 TextInput 组件)的基础。
文本字段内容可以在 SWF 文件中预先指定、从外部源(如文本文件或数据库)中加载或由用户在与应用程序交互时输入。在文本字段内,文本可以显示为呈现的 HTML 内容,并可在其中嵌入图像。一旦建立了文本字段的实例,您可以使用 flash.text 包中的类(例如 TextFormat 类和 StyleSheet 类)来控制文本的外观。flash.text 包几乎包含与在 ActionScript 中创建文本、管理文本及对文本进行格式设置有关的所有类。
可以用 TextFormat 对象定义格式设置并将此对象分配给文本字段,以此来设置文本格式。如果文本字段包含 HTML 文本,则可以对文本字段应用 StyleSheet 对象,以便将样式分配给文本字段内容的特定片段。TextFormat 对象或 StyleSheet 对象包含定义文本外观(例如颜色、大小和粗细)的属性。TextFormat 对象可以将属性分配给文本字段中的所有内容,也可以分配给某个范围的文本。
重要概念和术语
层叠样式表 (Cascading style sheet):对以 XML(或 HTML)格式构成的内容指定样式和格式设置的标准语法。
设备字体 (Device font):安装在用户计算机上的字体。
动态文本字段 (Dynamic text field):可以由 ActionScript 更改内容而不能由用户输入内容的文本字段。
嵌入字体 (Embedded font):字符轮廓数据存储在应用程序的 SWF 文件中的一种字体。
HTML 文本:使用 ActionScript 输入到文本字段中的文本内容,包括 HTML 格式标签和实际文本内容。
输入文本字段 (Input text field):其内容可通过用户输入也可以通过 ActionScript 进行更改的文本字段。
静态文本字段 (Static text field):在 Flash 创作工具中创建的文本字段,运行 SWF 文件时不能更改其内容。
文本行量度 (Text line metri):文本字段中文本内容不同部分的大小的量度,如文本的基线、字符顶部的高度、下行字符(某些小写字母延伸到基线以下的部分)的大小等等。
显示文本
尽管诸如 Adobe Flex Builder 和 Flash 等创作工具为显示文本提供了几种不同的选择(包括与文本相关的组件或文本工具),但以编程方式显示文本的主要途径还是通过文本字段。
文本类型
文本字段中文本的类型根据其来源进行划分:
动态文本
动态文本包含从外部源(例如文本文件、XML 文件以及远程 Web 服务)加载的内容。
输入文本
输入文本是指用户输入的任何文本或用户可以编辑的动态文本。可以设置样式表来设置输入文本的格式,或使用 flash.text.TextFormat 类为输入内容指定文本字段的属性。
静态文本
静态文本只能通过 Flash 创作工具来创建。您无法使用 ActionScript 3.0 创建静态文本实例。但是,可以使用 ActionScript 类(例如 StaticText 和 TextSnapshot)来操作现有的静态文本实例。
修改文本字段内容
可以通过将一个字符串赋予 flash.text.TextField.text 属性来定义动态文本。
或者,可以将一个远程变量的值赋予 text 属性。从远程源加载文本值有三种方式:
flash.net.URLLoader 和 flash.net.URLRequest 类可以从本地或远程位置为文本加载变量。
FlashVars 属性被嵌入到承载 SWF 文件的 HTML 页中,可以包含文本变量的值。
flash.net.SharedObject 类管理值的永久存储。
显示HTML文本
flash.text.TextField 类具有一个 htmlText 属性,可使用它将您的文本字符串标识为包含用于设置内容格式的 HTML 标签。如下例所示,必须将您的字符串值赋予 htmlText 属性(而不是 text 属性),以便 Flash Player 将文本呈现为 HTML.
一旦您使用 htmlText 属性指定了内容,就可以使用样式表或 textformat 标签来管理内容的格式设置。
在文本字段中使用图像
将内容显示为 HTML 文本的另一个好处是可以在文本字段中包括图像。可以使用 img 标签引用一个本地或远程图像,并使其显示在关联的文本字段内。img 标签支持 JPEG、GIF、PNG 和 SWF 文件。
在文本字段中滚动文本
在许多情况下,文本会比显示该文本的文本字段长。或者,某个输入字段允许用户输入比字段一次可显示的文本内容更多的文本。您可以使用 flash.text.TextField 类的与滚动相关的属性来管理过长的内容(垂直或水平方向)。
与滚动有关的属性包括 TextField.scrollV、TextField.scrollH、maxScrollV 和 maxScrollH。可使用这些属性来响应鼠标单击或按键等事件。
选择和操作文本
您可以选择动态文本或输入文本。由于 TextField 类的文本选择属性和方法使用索引位置来设置要操作的文本的范围,因此即使不知道内容,您也可以以编程方式选择动态文本或输入文本。
选择文本
默认情况下,flash.text.TextField.selectable 属性为 true,您可以使用 setSelection() 方法以编程方式选择文本。
捕获用户选择的文本
TextField 类的 selectionBeginIndex 和 selectionEndIndex 属性可用于捕获用户当前选择的内容,这两个属性为"只读"属性,因此不能设置为以编程方式选择文本。此外,输入文本字段也可以使用 caretIndex 属性。
捕获文本输入
默认情况下,文本字段的 type 属性设置为 dynamic。如果使用 TextFieldType 类将 type 属性设置为 input,则可以收集用户输入并保存该值以便在应用程序的其它部分使用。对于表单以及希望用户定义可用于程序中其它位置的文本值的任何应用程序而言,输入文本字段都十分有用。
限制文本输入
由于输入文本字段经常用于表单或应用程序中的对话框,因此您可能想要限制用户在文本字段中输入的字符的类型,或者甚至想将文本隐藏。可以设置 flash.text.TextField 类的 displayAsPassword 属性和 restrict 属性来控制用户输入。
displayAsPassword 属性只是在用户键入文本时将其隐藏(显示为一系列星号)。当 displayAsPassword 设置为 true 时,"剪切"和"复制"命令及其对应的键盘快捷键将不起作用。
restrict 属性则更为复杂一些,因为您需要指定允许用户在输入文本字段中键入哪些字符。可以允许特定字母、数字或字母、数字和字符的范围。ActionScript 3.0 使用连字符来定义范围,使用尖号来定义被排除的字符。
设置文本格式
以编程方式设置文本显示的格式设置有多种方式。可以直接在 TextField 实例中设置属性,例如,TextFIeld.thickness、TextField.textColor 和 TextField.textHeight 属性。也可以使用 htmlText 属性指定文本字段的内容,并使用受支持的 HTML 标签,如 b、i 和 u。但是您也可以将 TextFormat 对象应用于包含纯文本的文本字段,或将 StyleSheet 对象应用于包含 htmlText 属性的文本字段。使用 TextFormat 和 StyleSheet 对象可以对整个应用程序的文本外观提供最有力的控制和最佳的一致性。可以定义 TextFormat 或 StyleSheet 对象并将其应用于应用程序中的部分或所有文本字段。
指定文本格式
您可以使用 TextFormat 类设置多个不同的文本显示属性,并将它们应用于 TextField 对象的整个内容或一定范围的文本。
TextField.setTextFormat() 方法只影响已显示在文本字段中的文本。如果 TextField 中的内容发生更改,则应用程序可能需要重新调用 TextField.setTextFormat() 方法以便重新应用格式设置。您也可以设置 TextField 对象的 defaultTextFormat 属性来指定要用于用户输入文本的格式。
应用层叠样式表
文本字段可以包含纯文本或 HTML 格式的文本。纯文本存储在实例的 text 属性中,而 HTML 文本存储在 htmlText 属性中。
您可以使用 CSS 样式声明来定义可应用于多种不同文本字段的文本样式。CSS 样式声明可以在应用程序代码中进行创建,也可以在运行时从外部 CSS 文件中加载。
flash.text.StyleSheet 类用于处理 CSS 样式。StyleSheet 类可识别有限的 CSS 属性集合。
要使 CSS 样式生效,应在设置 htmlText 属性之前对 TextField 对象应用样式表。
根据设计,带有样式表的文本字段是不可编辑的。如果您有一个输入文本字段并为其分配一个样式表,则该文本字段将显示样式表的属性,但不允许用户在其中输入新的文本。而且,您也无法在分配有样式表的文本字段上使用以下 ActionScript API:
TextField.replaceText() 方法
TextField.replaceSelectedText() 方法
TextField.defaultTextFormat 属性
TextField.setTextFormat() 方法
如果某个文本字段已经分配了一个样式表,但后来将 TextField.styleSheet 属性设置为 null,则 TextField.text 和 TextField.htmlText 属性的内容会向它们的内容中添加标签和属性,以结合先前分配的样式表设定的格式。若要保留原始 htmlText 属性,应在将样式表设置为 null 之前将其保存在变量中。
加载外部 CSS 文件
用于设置格式的 CSS 方法的功能更加强大,您可以在运行时从外部文件加载 CSS 信息。当 CSS 数据位于应用程序本身以外时,您可以更改应用程序中的文本的可视样式,而不必更改 ActionScript 3.0 源代码。部署完应用程序后,可以通过更改外部 CSS 文件来更改应用程序的外观,而不必重新部署应用程序的 SWF 文件。
StyleSheet.parseCSS() 方法可将包含 CSS 数据的字符串转换为 StyleSheet 对象中的样式声明。
设置文本字段内文本范围的格式
flash.text.TextField 类的一个特别有用的方法是 setTextFormat() 方法。使用 setTextFormat(),您可以将特定属性分配给文本字段的部分内容以响应用户输入 。
高级文本呈现
ActionScript 3.0 在 flash.text 包中提供多个类来控制所显示文本的属性,包括嵌入字体、消除锯齿设置、alpha 通道控制及其它特定设置。
使用嵌入字体
您在应用程序中为 TextField 指定特定字体时,Flash Player 会查找具有相同名称的设备字体(位于用户计算机上的字体)。如果在用户系统上没有找到该字体,或者如果用户的字体版本与具有该名称的字体略有差异,则文本显示外观会与预想的情况差别很大。
若要确保用户看到完全正确的字体,您可以将该字体嵌入到应用程序的 SWF 文件中。嵌入字体有很多好处:
嵌入字体字符是消除锯齿的,特别是对于较大的文本,该字体可以使文本边缘看起来更平滑。
可以旋转使用嵌入字体的文本。
嵌入字体文本可以产生透明或半透明效果。
可以对嵌入字体使用字距调整的 CSS 样式。
使用嵌入字体的最大限制是嵌入字体会增加文件大小或应用程序的下载大小。
将声音文件嵌入到应用程序的 SWF 文件中的具体方法因开发环境而异。
嵌入字体后,可以确保 TextField 使用正确的嵌入字体:
将 TextField 的 embedFonts 属性设置为 true。
创建一个 TextFormat 对象,将其 fontFamily 属性设置为嵌入字体的名称,并对 TextField 应用 TextFormat 对象。指定嵌入字体时,fontFamily 属性应只包含一个名称;该名称不能是用逗号分隔的由多个字体名称构成的列表。
如果使用 CSS 样式为 TextField 或组件设置字体,请将 font-family CSS 属性设置为嵌入字体的名称。如果要指定一种嵌入字体,则 font-family 属性必须包含单一名称,而不能是多个名称的列表。
控制清晰度、粗细和消除锯齿
默认情况下,在文本调整大小、更改颜色或在不同背景上显示时,Flash Player 可以确定文本显示控件的设置(如清晰度、粗细和消除锯齿)。在某些情况下,如文本很小、很大或显示在各种特别的背景上时,您可能需要保持您自己对这些设置的控制。可以使用 flash.text.TextRenderer 类及其相关类(如 CSMSettings 类)来覆盖 Flash Player 设置。使用这些类可以精确控制嵌入文本的呈现品质。
flash.text.TextField.antiAliasType 属性必须具有 AntiAliasType.ADVANCED 值(该值为默认值),以供您设置清晰度、粗细或 gridFitType 属性,或供您使用 TextRenderer.setAdvancedAntiAliasingTable() 方法。
处理静态文本
静态文本只能在 Flash 创作工具中创建。不能使用 ActionScript 以编程方式对静态文本进行实例化。静态文本用于比较短小并且不会更改(而动态文本则会更改)的文本。可以将静态文本看作类似于在 Flash 创作工具中在舞台上绘制的圆或正方形的一种图形元素。由于静态文本比动态文本受到更多的限制,ActionScript 3.0 不支持使用 flash.text.StaticText 类读取静态文本属性值的能力。另外,您可以使用 flash.text.TextSnapshot 类从静态文本中读取值。
使用 StaticText 类访问静态文本字段
通常,可以在 Flash 创作工具的"动作"面板中使用 flash.text.StaticText 类来与放置在舞台上的静态文本实例进行交互。也可以在与包含静态文本的 SWF 文件进行交互的 ActionScript 文件中执行类似工作。但是这两种情况下都不能以编程方式对静态文本实例进行实例化。静态文本是在 Flash CS3 创作工具中创建的。
引用静态文本字段后,您可以在 ActionScript 3.0 中使用该字段的属性。
使用 TextSnapshot 类
如果要以编程方式使用现有静态文本实例,可以使用 flash.text.TextSnapshot 类来与 flash.display.DisplayObjectContainer 的 textSnapshot 属性配合工作。也就是说,通过 DisplayObjectContainer.textSnapshot 属性创建 TextSnapshot 实例。然后,可以将方法应用于该实例,以检索值或选择部分静态文本。
处理位图
处理位图的基本知识
处理位图简介
使用数字图像时,您可能会遇到两种主要的图形类型:位图和矢量。位图图形也称为光栅图形,由排列为矩形网格形式的小方块(像素)组成。矢量图形由以数学方式生成的几何形状(如直线、曲线和多边形)组成。
位图图像用图像的宽度和高度来定义,以像素为量度单位,每个像素包含的位数表示像素包含的颜色数。在使用 RGB 颜色模型的位图图像中,像素由三个字节组成:红、绿和蓝。每个字节包含一个 0 至 255 之间的值。将字节与像素合并时,它们可以产生与艺术混合绘画颜色相似的颜色。例如,一个包含红色字节值 255、绿色字节值 102 和蓝色字节值 0 的像素可以形成明快的橙色。
位图图像的品质由图像分辨率和颜色深度位值共同确定。分辨率 与图像中包含的像素数有关。像素数越大,分辨率越高,图像也就越精确。颜色深度 与像素可包含的信息量有关。例如,颜色深度值为每像素 16 位的图像无法显示颜色深度为 48 位的图像所具有颜色数。因此,48 位图像与 16 位图像相比,其阴影具有更高的平滑度。
由于位图图形跟分辨率有关,因此不能很好地进行缩放。当放大位图图像时,这一特性显得尤为突出。通常,放大位图有损其细节和品质。
位图文件格式
位图图像可分为几种常见的文件格式。这些格式使用不同类型的压缩算法减小文件大小,并基于图像的最终用途优化图像品质。Adobe Flash Player 支持的位图图像格式有 GIF、JPG 和 PNG。
GIF
图形交换格式 (GIF) 最初由 CompuServe 于 1987 年开发,作为一种传送 256 色(8 位颜色)图像的方式。此格式提供较小的文件大小,是基于 Web 的图像的理想格式。受此格式的调色板所限,GIF 图像通常不适用于照片,照片通常需要高度的阴影和颜色渐变。GIF 图像允许产生一位透明度,允许将颜色映射为清晰(或透明)。这可以使网页的背景颜色通过已映射透明度的图像显示出来。
JPEG
由联合图像专家组 (JPEG) 开发,JPEG(通常写成 JPG)图像格式使用有损压缩算法允许 24 位颜色深度具有很小的文件大小。有损压缩意味着每次保存图像,都会损失图像品质和数据,但会生成更小的文件大小。由于 JPEG 能够显示数百万计的颜色,因此它是照片的理想格式。控制应用于图像的压缩程度的功能使您能够控制图像品质和文件大小。
PNG
可移植网络图形 (PNG) 格式是作为受专利保护的 GIF 文件格式的开放源替代格式而开发的。PNG 最多支持 64 位颜色深度,允许使用最多 1600 万种颜色。由于 PNG 是一种比较新的格式,因此一些旧版本浏览器不支持 PNG 文件。与 JPG 不同,PNG 使用无损压缩,这意味着保存图像时不会丢失图像数据。PNG 文件还支持 Alpha 透明度,允许使用最多 256 级透明度。
透明位图和不透明位图
使用 GIF 或 PNG 格式的位图图像可以对每个像素添加一个额外字节(Alpha 通道)。此额外像素字节表示像素的透明度值。
GIF 图像允许使用一位透明度,这意味着您可以在 256 色调色板中指定一种透明的颜色。而 PNG 图像最多可以有 256 级透明度。当需要将图像或文本混合到背景中时,此功能特别有用。
ActionScript 3.0 在 BitmapData 类中复制了此额外透明度像素字节。与 PNG 透明度模型类似,BitmapDataChannel.ALPHA 常量最多提供 256 级透明度。
重要概念和术语
Alpha:颜色或图像中的透明度级别(更准确地说是指不透明度)。Alpha 量通常称为"Alpha 通道"值。
ARGB 颜色:一种配色方案,其中每个像素的颜色是红、绿和蓝色值的混合颜色,并将其透明度指定为一个 Alpha 值。
颜色通道:通常,将颜色表示为几种基本颜色的混合颜色,对于计算机图形来说,通常是红色、绿色和蓝色。每种基本颜色都视为一个颜色通道;每个颜色通道中的颜色量混合在一起可确定最终颜色。
颜色深度:也称为"位深度",指专门用于每个像素的计算机内存量,因而可以确定图像中可以显示的可能颜色数。
像素:位图图像中的最小信息单位,实际上就是颜色点。
分辨率:图像的像素尺寸,它决定图像中包含的精细细节的级别。分辨率通常表示为用像素数表示的宽度和高度。
RGB 颜色:一种配色方案,其中每个像素的颜色均表示为红、绿和蓝色值的混合颜色。
Bitmap 和 BitmapData 类
处理位图图像的主要 ActionScript 3.0 类是 Bitmap 类(用于在屏幕上显示位图图像)和 BitmapData 类(用于访问和操作位图的原始图像数据)。
了解 Bitmap 类
作为 DisplayObject 类的子类,Bitmap 类是用于显示位图图像的主要 ActionScript 3.0 类。这些图像可能已经通过 flash.display.Loader 类加载到 Flash 中,或已经使用 Bitmap() 构造函数动态创建。从外部源加载图像时,Bitmap 对象只能使用 GIF、JPEG 或 PNG 格式的图像。实例化后,可将 Bitmap 实例视为需要呈现在舞台上的 BitmapData 对象的包装。由于 Bitmap 实例是一个显示对象,因此可以使用显示对象的所有特性和功能来操作 Bitmap 实例。
像素贴紧和平滑
除了所有显示对象常见的功能外,Bitmap 类还提供了特定于位图图像的一些附加功能。
与 Flash 创作工具中的贴紧像素功能类似,Bitmap 类的 pixelSnapping 属性可确定 Bitmap 对象是否贴紧最近的像素。此属性接受 PixelSnapping 类中定义的三个常量之一:ALWAYS、AUTO 和 NEVER。
应用像素贴紧的语法为: myBitmap.pixelSnapping = PixelSnapping.ALWAYS;
通常,缩放位图图像时,图像会变得模糊或扭曲。若要帮助减少这种扭曲,请使用 BitmapData 类的 smoothing 属性。这是一个布尔值属性,设置为 true 时,缩放图像时,可使图像中的像素平滑或消除锯齿。它可使图像更加清晰、更加自然。
了解 BitmapData 类
BitmapData 类位于 flash.display 包中,它可以看作是加载的或动态创建的位图图像中包含的像素的照片快照。此快照用对象中的像素数据的数组表示。BitmapData 类还包含一系列内置方法,可用于创建和处理像素数据。
若要实例化 BitmapData 对象,请使用以下代码:
var myBitmap:BitmapData = new BitmapData(width:Number, height:Number, transparent:Boolean, fillColor:uinit);
width 和 height 参数指定位图的大小;二者的最大值都是 2880 像素。transparent 参数指定位图数据是 (true) 否 (false) 包括 Alpha 通道。fillColor 参数是一个 32 位颜色值,它指定背景颜色和透明度值(如果设置为 true)。
若要在屏幕上呈现新创建的 BitmapData 对象,请将此对象分配给或包装到 Bitmap 实例中。为此,可以作为 Bitmap 对象的构造函数的参数形式传递 BitmapData 对象,也可以将此对象分配给现有 Bitmap 实例的 bitmapData 属性。您还必须通过调用将包含该 Bitmap 实例的显示对象容器的 addChild() 或 addChildAt() 方法将该 Bitmap 实例添加到显示列表中。
处理像素
BitmapData 类包含一组用于处理像素数据值的方法。
处理单个像素
在像素级别更改位图图像的外观时,您首先需要获取要处理的区域中包含的像素的颜色值。使用 getPixel() 方法可读取这些像素值。
getPixel() 方法从作为参数传递的一组 x, y(像素)坐标中检索 RGB 值。如果您要处理的像素包括透明度(Alpha 通道)信息,则需要使用 getPixel32() 方法。此方法也可以检索 RGB 值,但与 getPixel() 不同,getPixel32() 返回的值包含表示所选像素的 Alpha 通道(透明度)值的附加数据。
或者,如果只想更改位图中包含的某个像素的颜色或透明度,则可以使用 setPixel() 或 setPixel32() 方法。若要设置像素的颜色,只需将 x, y 坐标和颜色值传递到这两种方法之一即可。
像素级别冲突检测
BitmapData.hitTest() 方法可以在位图数据和另一个对象或点之间执行像素级别冲突检测。
BitmapData.hitTest() 方法接受五个参数:
firstPoint (Point):此参数指在其上执行点击测试的第一个 BitmapData 的左上角的像素位置。
firstAlphaThreshold (uint):此参数指定对于此点击测试视为不透明的最高 Alpha 通道值。
secondObject (Object):此参数表示影响区域。secondObject 对象可以是 Rectangle、Point、Bitmap 或 BitmapData 对象。此对象表示在其上执行冲突检测的点击区域。
secondBitmapDataPoint (Point):此可选参数用于在第二个 BitmapData 对象中定义像素位置。只有当 secondObject 的值为 BitmapData 对象时,才使用此参数。默认值为 null。
secondAlphaThreshold (uint):此可选参数表示在第二个 BitmapData 对象中视为不透明的最高 Alpha 通道值。默认值为 1。只有当 secondObject 是一个 BitmapData 对象且两个 BitmapData 对象都透明时,才使用此参数。
在不透明图像上执行冲突检测时,请牢记,ActionScript 会将图像视为完全不透明的矩形(或边框)。或者,在透明的图像上执行像素级别点击测试时,需要两个图像都是透明的。除此之外,ActionScript 还使用 Alpha 阈值参数来确定像素在哪点开始从透明变为不透明。
复制位图数据
若要从一个图像向另一个图像中复制位图数据,可以使用多种方法:clone()、copyPixels()、copyChannel() 和 draw()。
正如名称的含义一样,clone() 方法允许您将位图数据从一个 BitmapData 对象克隆或采样到另一个对象。调用此方法时,此方法返回一个新的 BitmapData 对象,它是与被复制的原始实例完全一样的克隆。
copyPixels() 方法是一种从一个 BitmapData 对象向另一个对象复制像素的快速简便的方法。该方法会拍摄源图像的矩形快照(由 sourceRect 参数定义),并将其复制到另一个矩形区域(大小相等)。新"粘贴"的矩形位置在 destPoint 参数中定义。
copyChannel() 方法从源 BitmapData 对象中采集预定义的颜色通道值(Alpha、红、绿或蓝),并将此值复制到目标 BitmapData 对象的通道中。调用此方法不会影响目标 BitmapData 对象中的其它通道。
draw() 方法将源 sprite、影片剪辑或其它显示对象中的图形内容绘制或呈现在新位图上。使用 matrix、colorTransform、blendMode 和目标 clipRect 参数,可以修改新位图的呈现方式。此方法使用 Flash Player 矢量渲染器生成数据。
如果源对象在最初加载后应用了变形(颜色、矩阵等等),则不能将这些变形复制到新对象。如果想要将变形复制到新位图,则需要将 transform 属性的值从原始对象复制到使用新 BitmapData 对象的 Bitmap 对象的 transform 属性中。
使用杂点功能制作纹理
若要修改位图的外观,可以使用 noise() 方法或 perlinNoise() 方法对位图应用杂点效果。可以把杂点效果比作未调谐的电视屏幕的静态外观。
若要对位图应用杂点效果,请使用 noise() 方法。此方法对位图图像的指定区域中的像素应用随机颜色值。
此方法接受五个参数:
randomSeed (int):决定图案的随机种子数。不管名称具有什么样的含义,只要传递的数字相同,此数字就会生成相同的结果。为了获得真正的随机结果,请使用 Math.random() 方法为此参数传递随机数字。
low (uint):此参数指要为每个像素生成的最低值(0 至 255)。默认值为 0。将此参数设置为较低值会产生较暗的杂点图案,而将此参数设置为较高值会产生较亮的图案。
high (uint):此参数指要为每个像素生成的最高值(0 至 255)。默认值为 255。将此参数设置为较低值会产生较暗的杂点图案,而将此参数设置为较高值会产生较亮的图案。
channelOptions (uint):此参数指定将向位图对象的哪个颜色通道应用杂点图案。此数字可以是四个颜色通道 ARGB 值的任意组合。默认值是 7。
grayScale (Boolean):设置为 true 时,此参数对位图像素应用 randomSeed 值,可有效地褪去图像中的所有颜色。此参数不影响 Alpha 通道。默认值为 false。
如果想要创建更好的有机外观纹理,请使用 perlinNoise() 方法。perlinNoise() 方法可生成逼真、有机的纹理,是用于烟雾、云彩、水、火或爆炸的理想图案。
由于 perlinNoise() 方法是由算法生成的,因此它使用的内存比基于位图的纹理少。但还是会对处理器的使用有影响,特别是对于旧计算机,会降低 Flash 内容的处理速度,使屏幕重新绘制的速度比帧频慢。这主要是因为需要进行浮点计算,以便处理 Perlin 杂点算法。
此方法接受九个参数(前六个是必需参数):
baseX (Number):决定创建的图案的 x(大小)值。
baseY (Number):决定创建的图案的 y(大小)值。
numOctaves (uint):要组合以创建此杂点的 octave 函数或各个杂点函数的数目。octave 数目越大,创建的图像越精细,但这需要更多的处理时间。
randomSeed (int):随机种子数的功能与在 noise() 函数中的功能完全相同。为了获得真正的随机结果,请使用 Math.random() 方法为此参数传递随机数字。
stitch (Boolean):如果设置为 true,则此方法尝试缝合(或平滑)图像的过渡边缘以形成无缝的纹理,用于作为位图填充进行平铺。
fractalNoise (Boolean):此参数与此方法生成的渐变的边缘有关。如果设置为 true,则此方法生成的碎片杂点会对效果的边缘进行平滑处理。如果设置为 false,则将生成湍流。带有湍流的图像具有可见的不连续性渐变,可以使用它处理更接近锐化的视觉效果,例如,火焰或海浪。
channelOptions (uint):channelOptions 参数的功能与在 noise() 方法中的功能完全相同。它指定对哪个颜色通道(在位图上)应用杂点图案。此数字可以是四个颜色通道 ARGB 值的任意组合。默认值是 7。
grayScale (Boolean):grayScale 参数的功能与在 noise() 方法中的功能完全相同。如果设置为 true,则对位图像素应用 randomSeed 值,可有效地褪去图像中的所有颜色。默认值为 false。
offsets (Array):对应于每个 octave 的 x 和 y 偏移的点数组。通过处理偏移值,可以平滑滚动图像层。偏移数组中的每个点将影响一个特定的 octave 杂点函数。默认值为 null。
滚动位图
scroll() 方法可以复制屏幕上的位图,然后将它粘贴到由 (x, y) 参数指定的新偏移位置。如果位图的一部分恰巧在舞台以外,则会产生图像发生移位的效果。与计时器函数(或 enterFrame 事件)配合使用时,可以使图像呈现动画或滚动效果。
处理视频
视频基础知识
视频处理简介
Adobe Flash Player 的一个重要功能是可以使用 ActionScript,以操作其它可视内容(如图像、动画、文本等)的方式显示和操作视频信息。
在 Adobe Flash CS3 Professional 中创建 Flash 视频 (FLV) 文件时,您可以选择视频的外观,包括常用的回放控件。不过,您不一定要局限于可用的选项。使用 ActionScript 可以精确控制视频的加载、显示和回放,这意味着您可以创建自己的视频播放器外观,也可以按照所需的任何非传统方式使用视频。
在 ActionScript 中使用视频涉及多个类的联合使用:
Video 类:舞台上的实际视频内容框是 Video 类的一个实例。Video 类是一种显示对象,因此可以使用适用于其它显示对象的同样的技术(比如定位、应用变形、应用滤镜和混合模式等)进行操作。
NetStream 类:在加载将由 ActionScript 控制的视频文件时,将使用一个 NetStream 实例来表示该视频内容的源,在本例中为视频数据流。使用 NetStream 实例也涉及 NetConnection 对象的使用,该对象是到视频文件的连接,它好比是视频数据馈送的通道。
Camera 类:在通过连接到用户计算机的摄像头处理视频数据时,会使用一个 Camera 实例来表示视频内容的源,即用户的摄像头和它所提供的视频数据。
在加载外部视频时,您可以从标准 Web 服务器加载文件以便进行渐进式下载回放,也可以使用由专门的服务器(如 Adobe 的 Macromedia® Flash® Media Server)传送的视频流。
重要概念和术语
提示点:一个可以放在视频文件内特定时刻的标记,例如,可用作书签以便定位到该时刻或提供与该时刻相关联的其它数据。
编码:以一种格式接收视频数据并将其转换为另一种视频数据格式的过程;例如,接收高分辨率的源视频并将其转换为适合于 Internet 传送的格式。
帧:单一的一段视频信息;每一帧都类似于一个代表某一时刻的快照的静止图像。通过按顺序高速播放各个帧,可产生动画视觉效果。
关键帧:包含帧的完整信息的视频帧。关键帧后面的其它帧仅包含有关它们与关键帧之间的差异的信息,而不包含完整的帧信息。
元数据:有关视频文件的信息,可嵌入在视频文件中并可在加载视频时检索。
渐进式下载:当视频文件从标准 Web 服务器传送时,会使用渐进式下载来加载视频数据,这意味着会按顺序加载视频信息。其好处是不必等待整个文件下载完毕即可开始播放视频;不过,它会阻止您向前跳到视频中尚未加载的部分。
流式传输:渐进式下载的一种替代方法,使用流式传输(有时称为"实流")技术和一台专用视频服务器通过 Internet 传送视频。使用流式传输,用于查看视频的计算机不必一次下载整个视频。为了加快下载速度,在任何时刻,计算机均只需要整个视频信息的一部分。由于使用一台专用服务器来控制视频内容的传送,因此可以在任何时刻访问视频的任何部分,而无需等待其下载完毕后才能进行访问。
了解 Flash 视频 (FLV) 格式
FLV 文件格式包含用 Flash Player 编码以便于传送的音频和视频数据。例如,如果您有 QuickTime 或 Windows Media 视频文件,便可使用编码器(如 Flash Video Encoder 或 Sorenson™ Squeeze)将该文件转换为 FLV 文件。
可以通过将视频导入到 Flash 创作工具,然后导出为 FLV 文件来创建 FLV 文件。可以使用"FLV 导出"插件从受支持的视频编辑应用程序中导出 FLV 文件。
使用外部 FLV 文件可以提供使用导入的视频时不可用的某些功能:
无需降低回放速度就可以在 Flash 文档中使用较长的视频剪辑。可以使用缓存内存的方式来播放外部 FLV 文件,这意味着可以将大型文件分成若干个小片段存储,对其进行动态访问,这种方式比嵌入的视频文件所需的内存更少。
外部 FLV 文件可以和它所在的 Flash 文档具有不同的帧速率。例如,可以将 Flash 文档帧速率设置为 30 帧/秒 (fps),并将视频帧速率设置为 21 fps。与嵌入的视频相比,此项设置可使您更好地控制视频,确保视频顺畅地回放。此项设置还允许您在不改变现有 Flash 内容的前提下以不同的帧速率播放 FLV 文件。
利用外部 FLV 文件,Flash 文档回放就不必在视频文件进行加载时中断。导入的视频文件有时可能需要中断文档回放来执行某些功能,例如,访问 CD-ROM 驱动器。FLV 文件可以独立于 Flash 文档执行功能,因此不会中断回放。
对于外部 FLV 文件,为视频内容加字幕更加简单,这是因为您可以使用事件处理函数访问视频的元数据。
若要从 Web 服务器加载 FLV 文件,则可能需要向您的 Web 服务器注册文件扩展名和 MIME 类型;请查看您的 Web 服务器文档。FLV 文件的 MIME 类型是 video/x-flv。
了解 Video 类
使用 Video 类可以直接在应用程序中显示实时视频流,而无需将其嵌入 SWF 文件中。可以使用 Camera.getCamera() 方法捕获并播放实时视频。还可以使用 Video 类通过 HTTP 或在本地文件系统中回放 FLV 文件。在项目中使用 Video 有多种不同方法:
使用 NetConnection 和 NetStream 类动态加载 FLV 并在 Video 对象中显示视频。
从用户摄像头捕获输入。
使用 FLVPlayback 组件。
尽管 Video 类位于 flash.media 包中,但它继承自 flash.display.DisplayObject 类,因此,所有显示对象功能(如矩阵转换和滤镜)也适用于 Video 实例。
加载视频文件
使用 NetStream 和 NetConnection 类加载视频是一个多步骤过程:
第一步是创建一个 NetConnection 对象。如果连接到没有使用服务器(如 Adobe 的 Flash Media Server 2 或 Adobe Flex)的本地 FLV 文件,则使用 NetConnection 类可通过向 connect() 方法传递值 null,来从 HTTP 地址或本地驱动器播放流式 FLV 文件。
var nc:NetConnection = new NetConnection();
nc.connect(null);
第二步是创建一个 NetStream 对象(该对象将 NetConnection 对象作为参数)并指定要加载的 FLV 文件。以下代码片断将 NetStream 对象连接到指定的 NetConnection 实例,并加载 SWF 文件所在的目录中名为 video.flv 的 FLV:
var ns:NetStream = new NetStream(nc);
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
ns.play("video.flv");
function asyncErrorHandler(event:AsyncErrorEvent):void
{
// 忽略错误
}
第三步是创建一个新的 Video 对象,并使用 Video 类的 attachNetStream() 方法附加以前创建的 NetStream 对象。然后可以使用 addChild() 方法将该视频对象添加到显示列表中,如以下代码片断所示:
var vid:Video = new Video();
vid.attachNetStream(ns);
addChild(vid);
输入上面的代码后,Flash Player 将尝试加载 SWF 文件所在目录中的 video.flv 视频文件。
控制视频回放
NetStream 类提供了四个用于控制视频回放的主要方法:
pause():暂停视频流的回放。如果视频已经暂停,则调用此方法将不会执行任何操作。
resume():恢复回放暂停的视频流。如果视频已在播放,则调用此方法将不会执行任何操作。
seek():搜寻最接近指定位置(从流的开始位置算起的偏移量,以秒为单位)的关键帧。
togglePause():暂停或恢复流的回放。
注意:没有 stop() 方法。为了停止视频流,必须暂停回放并找到视频流的开始位置。play() 方法不会恢复回放,它用于加载视频文件。
检测视频流的末尾
为了侦听视频流的开始和末尾,需要向 NetStream 实例添加一个事件侦听器以侦听 netStatus 事件。
您专门想要侦听的两段代码为"NetStream.Play.Start"和"NetStream.Play.Stop",它们会在视频回放到达开始和末尾时发出信号。
通过侦听 netStatus 事件 (NetStatusEvent.NET_STATUS),您可以生成一个视频播放器,它在当前视频完成播放后加载播放列表中的下一个视频。
流式传输视频文件
若要流式传输 Flash Media Server 中的文件,可以使用 NetConnection 和 NetStream 类连接到远程服务器实例并播放指定的流。要指定实时消息传递协议 (RTMP) 服务器,请向 NetConnection.connect() 方法传递所需的 RTMP URL(例如"rtmp://localhost/appName/appInstance"),而不传递 null。若要播放指定 Flash Media Server 中的指定实时流或录制流,请为 NetStream.play() 方法传递一个由 NetStream.publish() 发布的实时数据的标识名称,或一个要回放的录制文件名称。有关详细信息,请参阅 Flash Media Server 文档。
了解提示点
并非所有 FLV 文件都包含提示点。虽然有在现有 FLV 文件中嵌入提示点的工具,但提示点通常是在 FLV 编码过程中嵌入在 FLV 文件中的。
您可以对 Flash 视频使用几种不同类型的提示点。可以使用 ActionScript 与在创建 FLV 文件时嵌入到 FLV 文件中的提示点进行交互,也可以与用 ActionScript 创建的提示点进行交互。
导航提示点:您可以在编码 FLV 文件时,将导航提示点嵌入到 FLV 流和 FLV 元数据包中。使用导航提示点可以使用户搜索到文件的指定部分。
事件提示点:您可以在编码 FLV 文件时,将事件提示点嵌入到 FLV 流和 FLV 元数据包中。还可以编写代码来处理在 FLV 回放期间于指定点上触发的事件。
ActionScript 提示点:使用 ActionScript 代码创建的外部提示点。您可以编写代码来触发这些与视频回放有关的提示点。这些提示点的精确度要低于嵌入的提示点(最高时相差 1/10 秒),因为视频播放器单独跟踪这些提示点。
由于导航提示点会在指定的提示点位置创建一个关键帧,因此可以使用代码将视频播放器的播放头移动到该位置。您可以在 FLV 文件中设置一些特殊点,让用户搜索这些点。例如,视频可能会具有多个章节或段,在这种情况下您就可以在视频文件中嵌入导航提示点,以此方式来控制视频。
如果您计划创建一个应用程序,希望用户能在其中导航至提示点,则应在编码文件时创建并嵌入提示点,而不应使用 ActionScript 提示点。您应将提示点嵌入到 FLV 文件中,因为这些提示点需要更加精确的处理。有关在 FLV 文件中嵌入提示点的详细信息,请参阅《使用 Flash》中的"嵌入提示点"。
您可以通过编写 ActionScript 来访问提示点参数。提示点参数是从 onCuePoint 回调处理函数接收的事件对象的一部分。
若要在视频到达特定提示点时在代码中触发特定动作,请使用 NetStream.onCuePoint 事件处理函数。
为 onCuePoint 和 onMetaData 编写回调方法
当播放器到达特定提示点或收到特定元数据时,您可以在应用程序中触发动作。若要触发此类动作,请使用 onCuePoint 和 onMetaData 事件处理函数。必须为这些处理函数编写回调方法,否则,Flash Player 可能会引发错误。
将 NetStream 对象的 client 属性设置为一个 Object
通过将 client 属性设置为一个 Object 或设置为 NetStream 的一个子类,可以重新发送 onMetaData 和 onCuePoint 回调方法或彻底忽略这些方法。
将 NetStream 对象的 client 属性设置为 this
通过将 client 属性设置为 this,Flash Player 会在当前范围内查找 onMetaData() 和 onCuePoint() 方法。调用 onMetaData 或 onCuePoint 回调处理函数时,如果不存在处理该回调的方法时,不会生成错误。
使用提示点
以下示例使用一个简单的 for..in 循环来遍历 onCuePoint 回调处理函数的 infoObject 参数中的每个属性。
使用视频元数据
您可以使用 onMetaData 回调处理函数来查看 FLV 文件中的元数据信息。元数据包含 FLV 文件的相关信息,如持续时间、宽度、高度和帧速率。添加到 FLV 文件中的元数据信息取决于编码 FLV 文件时所使用的软件或添加元数据信息时所使用的软件。
如果您的视频没有音频,则与音频相关的元数据信息(如 audiodatarate)将返回 undefined,因为在编码期间没有将音频信息添加到元数据中。
onMetaData 的信息对象
下表显示视频元数据的可能值
参数
描述
audiocodecid
一个数字,指示已使用的音频编解码器(编码/解码技术)。
audiodatarate
一个数字,指示音频的编码速率,以每秒千字节为单位。
audiodelay
一个数字,指示原始 FLV 文件的"time 0"在 FLV 文件中保持多长时间。为了正确同步音频,视频内容需要有少量的延迟。
canSeekToEnd
一个布尔值,如果 FLV 文件是用最后一帧(它允许定位到渐进式下载影片剪辑的末尾)上的关键帧编码的,则该值为 true。如果 FLV 文件不是用最后一帧上的关键帧编码的,则该值为 false。
cuePoints
嵌入在 FLV 文件中的提示点对象组成的数组,每个提示点对应一个对象。如果 FLV 文件不包含任何提示点,则值是未定义的。每个对象都具有以下属性:
Type、 name、 time 、parameters 。
duration
一个数字,以秒为单位指定 FLV 文件的持续时间。
framerate
一个数字,表示 FLV 文件的帧速率。
height
一个数字,以像素为单位表示 FLV 文件的高度。
videocodecid
一个数字,表示用于对视频进行编码的编解码器版本。
videodatarate
一个数字,表示 FLV 文件的视频数据速率。
width
一个数字,以像素为单位表示 FLV 文件的宽度。
下表显示 videocodecid 参数的可能值:
videocodecid
编解码器名称
2
Sorenson H.263
3
屏幕视频(仅限 SWF 7 和更高版本)
4
VP6(仅限 SWF 8 和更高版本)
5
带有 Alpha 通道的 VP6 视频(仅限 SWF 8 和更高版本)
下表显示 audiocodecid 参数的可能值:
audiocodecid
编解码器名称
0
未压缩
1
ADPCM
2
mp3
5
Nellymoser 8kHz 单声
6
Nellymoser
捕获摄像头输入
除了外部视频文件外,附加到用户计算机上的摄像头也可以作为您使用 ActionScript 进行显示和操作的视频数据的来源。Camera 类是 ActionScript 中内置的机制,用于使用计算机摄像头。
了解 Camera 类
使用 Camera 对象可以连接到用户的本地摄像头并在本地广播视频(回放给用户),或将其广播到远程服务器(比如 Flash Media Server)。
使用 Camera 类可以访问有关用户摄像头的以下各种信息:
Flash Player 可以使用用户计算机上安装的哪些摄像头
是否安装了摄像头
是否允许 Flash Player 访问用户摄像头
哪个摄像头当前处于活动状态
正在捕获的视频的宽度和高度
Camera 类包括多个有用的方法和属性,通过这些方法和属性可以使用 Camera 对象。
在屏幕上显示摄像头内容
连接到摄像头所需的代码比使用 NetConnection 和 NetStream 类加载 FLV 的代码少。由于需要有用户许可才能让 Flash Player 连接到摄像头,并且只有在连接到摄像头后才能访问摄像头,因此,Camera 类的使用可能很快就会变得非常麻烦。
以下代码演示如何使用 Camera 类连接到用户的本地摄像头:
var cam:Camera = Camera.getCamera();
var vid:Video = new Video();
vid.attachCamera(cam);
addChild(vid);
Camera 类不具有构造函数方法。若要创建新的 Camera 实例,请使用静态 Camera.getCamera() 方法。
设计摄像头应用程序
在编写需要连接到用户摄像头的应用程序时,需要在代码中考虑以下事项:
检查用户当前是否安装了摄像头。
检查用户是否显式允许 Flash Player 访问其摄像头。出于安全原因,播放器会显示"Flash Player 设置"对话框,让用户选择允许还是拒绝对其摄像头的访问。这样可以防止 Flash Player 在未经用户许可的情况下连接到其摄像头并广播视频流。如果用户单击允许,则应用程序即可连接到用户的摄像头。如果用户单击拒绝,则应用程序将无法访问用户的摄像头。应用程序始终应适当地处理这两种情况。
连接到用户摄像头
连接到用户摄像头时,执行的第一步是通过创建一个类型为 Camera 的变量并将其初始化为静态 Camera.getCamera() 方法的返回值来创建一个新的 Camera 实例。
下一步是创建一个新的视频对象并向其附加 Camera 对象。
第三步是向显示列表中添加该视频对象。由于 Camera 类不会扩展 DisplayObject 类,它不能直接添加到显示列表中,因此需要执行第 2 步和第 3 步。若要显示摄像头捕获的视频,需要创建一个新的视频对象并调用 attachCamera() 方法。
以下代码演示这三个步骤:
var cam:Camera = Camera.getCamera();
var vid:Video = new Video();
vid.attachCamera(cam);
addChild(vid);
注意,如果用户未安装摄像头,Flash Player 将不显示任何内容。
验证是否已安装摄像头
在尝试对 Camera 实例使用任何方法或属性之前,您需要验证用户是否已安装了摄像头。检查用户是否已安装摄像头有两种方式:
检查静态 Camera.names 属性,该属性包含可用摄像头名称的数组。此数组通常具有一个或几个字符串,因为多数用户不太可能同时安装多个摄像头。以下代码演示如何检查 Camera.names 属性以查看用户是否具有可用的摄像头:
if (Camera.names.length > 0)
{
trace("用户未安装摄像头。");
}
else
{
var cam:Camera = Camera.getCamera(); //获取默认摄像头。
}

检查静态 Camera.getCamera() 方法的返回值。如果没有摄像头可用或未安装摄像头,则此方法将返回 null,否则返回对 Camera 对象的引用。以下代码演示如何检查 Camera.getCamera() 方法以查看用户是否具有可用的摄像头:
var cam:Camera = Camera.getCamera();
if (cam == null)
{
trace("用户未安装摄像头。");
}
else
{
trace("用户至少安装了 1 个摄像头。");
}

由于 Camera 类不会扩展 DisplayObject 类,因此不能通过使用 addChild() 方法将它直接添加到显示列表中。为了显示摄像头捕获的视频,您需要创建一个新的 Video 对象并对 Video 实例调用 attachCamera() 方法。
以下代码片断演示在存在摄像头的情况下如何附加摄像头;如果不存在摄像头,Flash Player 将不显示任何内容:
var cam:Camera = Camera.getCamera();
if (cam != null)
{
var vid:Video = new Video();
vid.attachCamera(cam);
addChild(vid);
}
检测摄像头的访问权限
在可以显示摄像头输出之前,用户必须显式允许 Flash Player 访问该摄像头。在调用 attachCamera() 方法后,Flash Player 会显示"Flash Player 设置"对话框,提示用户允许或拒绝 Flash Player 访问摄像头或麦克风。如果用户单击"允许"按钮,则会在舞台上的 Video 实例中显示摄像头输出。如果用户单击"拒绝"按钮,则 Flash Player 将无法连接到摄像头,且 Video 对象将不显示任何内容。
如果想要检测用户是否允许访问其摄像头,可以侦听摄像头的 status 事件 (StatusEvent.STATUS),如以下代码所示:
var cam:Camera = Camera.getCamera();
if (cam != null)
{
cam.addEventListener(StatusEvent.STATUS, statusHandler);
var vid:Video = new Video();
vid.attachCamera(cam);
addChild(vid);
}
function statusHandler(event:StatusEvent):void
{
// 当用户在"Flash Player 设置"对话框中单击
// "允许"或"拒绝"按钮时调度此事件。
trace(event.code); //"Camera.Muted"或"Camera.Unmuted"
}
一旦用户单击"允许"或"拒绝"后,即会调用 statusHandler() 函数。使用以下两种方法之一可以检测用户单击了哪个按钮:
statusHandler() 函数的 event 参数包含一个 code 属性,其中包含字符串"Camera.Muted"或"Camera.Unmuted"。如果值为"Camera.Muted",则说明用户单击了"拒绝"按钮,Flash Player 将无法访问该摄像头。在下面的代码片段中您会看到此情况的一个示例:
function statusHandler(event:StatusEvent):void
{
switch (event.code)
{
case "Camera.Muted":
trace("用户单击了"拒绝"。");
break;
case "Camera.Unmuted":
trace("用户单击了"接受"。");
break;
}
}

Camera 类包含一个名为 muted 的只读属性,它可以指明用户在 Flash Player 的"隐私"面板中是拒绝访问摄像头 (true) 还是允许访问摄像头 (false)。在下面的代码片段中您会看到此情况的一个示例:
function statusHandler(event:StatusEvent):void
{
if (cam.muted)
{
trace("用户单击了"拒绝"。");
}
else
{
trace("用户单击了"接受"。");
}
}

通过检查将要调度的 status 事件,您可以编写处理用户接受或拒绝访问摄像头的代码并进行相应的清理。例如,如果用户单击"拒绝"按钮,则您可以向用户显示一条消息,说明他们如果想要参加视频聊天的话,需要单击"允许";或者,您也可以确保删除显示列表中的 Video 对象以释放系统资源。
最优化视频品质
默认情况下,Video 类的新实例为 320 像素宽乘以 240 像素高。为了最优化视频品质,应始终确保视频对象与 Camera 对象返回的视频具有相同的尺寸。使用 Camera 类的 width 和 height 属性,您可以获取 Camera 对象的宽度和高度,然后将该视频对象的 width 和 height 属性设置为与 Camera 对象的尺寸相符,也可以将 Camera 对象的宽度和高度传递给 Video 类的构造函数方法,如以下代码片断所示:
var cam:Camera = Camera.getCamera();
if (cam != null)
{
var vid:Video = new Video(cam.width, cam.height);
vid.attachCamera(cam);
addChild(vid);
}


由于 getCamera() 方法返回对 Camera 对象的引用(在没有可用摄像头时返回 null),因此,即使用户拒绝访问其摄像头,您也可以访问 Camera 对象的方法和属性。这样可以使用摄像头的本机高度和宽度设置视频实例的尺寸。
var vid:Video;
var cam:Camera = Camera.getCamera();

if (cam == null)
{
trace("找不到可用的摄像头。");
}
else
{
trace("找到摄像头: " + cam.name);
cam.addEventListener(StatusEvent.STATUS, statusHandler);
vid = new Video();
vid.attachCamera(cam);
}
function statusHandler(event:StatusEvent):void
{
if (cam.muted)
{
trace("无法连接到活动摄像头。");
}
else
{
// 调整 Video 对象的大小,使之与摄像头设置相符,并
// 将该视频添加到显示列表中。
vid.width = cam.width;
vid.height = cam.height;
addChild(vid);
}
// 删除 status 事件侦听器。
cam.removeEventListener(StatusEvent.STATUS, statusHandler);
}

监视回放条件
Camera 类包含多个属性,这些属性允许您监视 Camera 对象的当前状态。
向服务器发送视频
如果您要生成涉及 Video 或 Camera 对象的更为复杂的应用程序,可以使用 Flash Media Server 提供的流媒体功能和开发环境组合来创建媒体应用程序并将它提供给广泛的目标用户。开发人员可以使用这一组合来创建应用程序,如 Video on Demand、实时 Web 事件广播和 mp3 流,以及视频博客、视频消息传送和多媒体聊天环境。
处理声音
声音处理基础知识
处理声音简介
就像计算机可以采用数字格式对图像进行编码、将它们存储在计算机上以及检索它们以便在屏幕上显示它们一样,计算机可以捕获并编码数字音频(声音信息的计算机表示形式)以及对其进行存储和检索,以通过连接到计算机上的扬声器进行回放。一种回放声音的方法是使用 Adobe Flash Player 和 ActionScript。
将声音数据转换为数字形式后,它具有各种不同的特性,如声音的音量以及它是立体声还是单声道声音。在 ActionScript 中回放声音时,您也可以调整这些特性;例如,使声音变得更大,或者使其像是来自某个方向。
在 ActionScript 中控制声音之前,您需要先将声音信息加载到 Flash Player 中。可以使用四种方法将音频数据加载到 Flash Player 中,以便通过 ActionScript 对其进行使用。您可以将外部声音文件(如 mp3 文件)加载到 SWF 中;在创建 SWF 文件时将声音信息直接嵌入到其中;使用连接到用户计算机上的麦克风来获取音频输入,以及访问从服务器流式传输的声音数据。
从外部声音文件加载声音数据时,您可以在仍加载其余声音数据的同时开始回放声音文件的开头部分。
虽然可以使用各种不同的声音文件格式对数字音频进行编码,但是 ActionScript 3.0 和 Flash Player 支持以 mp3 格式存储的声音文件。它们不能直接加载或播放其它格式的声音文件,如 WAV 或 AIFF。
在 ActionScript 中处理声音时,您可能会使用 flash.media 包中的某些类。通过使用 Sound 类,您可以加载声音文件并开始回放以获取对音频信息的访问。开始播放声音后,Flash Player 可为您提供对 SoundChannel 对象的访问。由于已加载的音频文件只能是您在用户计算机上播放的几种声音之一,因此,所播放的每种单独的声音使用其自己的 SoundChannel 对象;混合在一起的所有 SoundChannel 对象的组合输出是实际通过计算机扬声器播放的声音。可以使用此 SoundChannel 实例来控制声音的属性以及将其停止回放。最后,如果要控制组合音频,您可以通过 SoundMixer 类对混合输出进行控制。
重要概念和术语
波幅 (Amplitude):声音波形上的点与零或平衡线之间的距离。
比特率 (Bit rate):每秒为声音文件编码或流式传输的数据量。对于 mp3 文件,比特率通常是以每秒千位数 (kbps) 来表述的。较高的比特率通常意味着较高品质的声音波形。
缓冲 (Buffering):在回放之前接收和存储声音数据。
mp3:MPEG-1 Audio Layer 3 (mp3) 是一种常用的声音压缩格式。
声相 (Panning):将音频信号放在立体声声场中左声道和右声道之间。
峰值 (Peak):波形中的最高点。
采样率 (Sampling rate):定义在生成数字信号时每秒从模拟音频信号采集的样本数。标准光盘音频的采样率为 44.1 kHz 或每秒 44,100 个样本。
流式传输 (Streaming):此过程是指,在仍从服务器加载声音文件或视频文件的后面部分的同时播放该文件的前面部分。
音量 (Volume):声音的响度。
波形 (Waveform):声音信号波幅随时间变化的图形形状。
了解声音体系结构
应用程序可以从以下四种主要来源加载声音数据:
在运行时加载的外部声音文件
在应用程序的 SWF 文件中嵌入的声音资源
来自连接到用户系统上的麦克风的声音数据
从远程媒体服务器流式传输的声音数据,如 Flash Media Server
可以在回放之前完全加载声音数据,也可以进行流式传输,即在仍进行加载的同时回放这些数据。
ActionScript 3.0 和 Flash Player 支持以 mp3 格式存储的声音文件。它们不能直接加载或播放其它格式的声音文件,如 WAV 或 AIFF。
使用 Adobe Flash CS3 Professional,可以导入 WAV 或 AIFF 声音文件,然后将其以 mp3 格式嵌入应用程序的 SWF 文件中。Flash 创作工具还可压缩嵌入的声音文件以减小文件大小,但会降低声音的品质。
ActionScript 3.0 声音体系结构使用 flash.media 包中的以下类。




描述
flash.media.Sound
Sound 类处理声音加载、管理基本声音属性以及启动声音播放。
flash.media.SoundChannel
当应用程序播放 Sound 对象时,将创建一个新的 SoundChannel 对象来控制回放。SoundChannel 对象控制声音的左和右回放声道的音量。播放的每种声音具有其自己的 SoundChannel 对象。
flash.media.SoundLoaderContext
SoundLoaderContext 类指定在加载声音时使用的缓冲秒数,以及 Flash Player 在加载文件时是否从服务器中查找跨域策略文件。SoundLoaderContext 对象用作 Sound.load() 方法的参数。
flash.media.SoundMixer
SoundMixer 类控制与应用程序中的所有声音有关的回放和安全属性。实际上,可通过一个通用 SoundMixer 对象将多个声道混合在一起,因此,该 SoundMixer 对象中的属性值将影响当前播放的所有 SoundChannel 对象。
flash.media.SoundTransform
SoundTransform 类包含控制音量和声相的值。可以将 SoundTransform 对象应用于单个 SoundChannel 对象、全局 SoundMixer 对象或 Microphone 对象等。
flash.media.ID3Info
ID3Info 对象包含一些属性,它们表示通常存储在 mp3 声音文件中的 ID3 元数据信息。
flash.media.Microphone
Microphone 类表示连接到用户计算机上的麦克风或其它声音输入设备。可以将来自麦克风的音频输入传送到本地扬声器或发送到远程服务器。Microphone 对象控制其自己的声音流的增益、采样率以及其它特性。
加载和播放的每种声音需要其自己的 Sound 类和 SoundChannel 类的实例。然后,全局 SoundMixer 类在回放期间将来自多个 SoundChannel 实例的输出混合在一起。
Sound、SoundChannel 和 SoundMixer 类不能用于从麦克风或流媒体服务器(如 Flash Media Server)中获取的声音数据。
加载外部声音文件
Sound 类的每个实例可加载并触发特定声音资源的回放。应用程序无法重复使用 Sound 对象来加载多种声音。如果它要加载新的声音资源,则应创建一个新的 Sound 对象。Sound() 构造函数接受一个 URLRequest 对象作为其第一个参数。当提供 URLRequest 参数的值后,新的 Sound 对象将自动开始加载指定的声音资源。
除了最简单的情况下,应用程序都应关注声音的加载进度,并监视在加载期间出现的错误。。较为稳妥的作法是等待声音完全加载后,再让用户执行可能启动声音播放的动作。
Sound 对象将在声音加载过程中调度多种不同的事件。应用程序可以侦听这些事件以跟踪加载进度,并确保在播放之前完全加载声音。下表列出了可以由 Sound 对象调度的事件。
事件
描述
open (Event.OPEN)
就在声音加载操作开始之前进行调度。
progress (ProgressEvent.PROGRESS)
从文件或流接收数据时,在声音加载过程中定期进行调度。
id3 (Event.ID3)
当存在可用于 mp3 声音的 ID3 数据时进行调度。
complete (Event.COMPLETE)
在加载了所有声音资源后进行调度。
ioError (IOErrorEvent.IO_ERROR)
在以下情况下进行调度:找不到声音文件,或者在收到所有声音数据之前加载过程中断。
监视声音加载过程
声音文件可能很大,而需要花很长时间进行加载。尽管 Flash Player 允许应用程序甚至在完全加载声音之前播放声音,但您可能需要向用户指示已加载了多少声音数据以及已播放了多少声音。
Sound 类调度以下两个事件,它们可使声音加载进度显示变得相对比较简单:ProgressEvent.PROGRESS 和 Event.COMPLETE。
Sound 对象上也提供了相同的 bytesLoaded 和 bytesTotal 属性。
处理嵌入的声音
对于用作应用程序用户界面中的指示器的较小声音(如在单击按钮时播放的声音),使用嵌入的声音非常有用(而不是从外部文件加载声音)。
处理声音流文件
如果在仍加载声音文件或视频文件数据的同时回放该文件,则认为是流式传输。通常,将对从远程服务器加载的外部声音文件进行流式传输,以使用户不必等待加载完所有声音数据再收听声音。
SoundMixer.bufferTime 属性表示 Flash Player 在允许播放声音之前应收集多长时间的声音数据(以毫秒为单位)。也就是说,如果将 bufferTime 属性设置为 5000,在开始播放声音之前,Flash Player 将从声音文件中加载至少相当于 5000 毫秒的数据。SoundMixer.bufferTime 默认值为 1000。
通过在加载声音时显式地指定新的 bufferTime 值,应用程序可以覆盖单个声音的全局 SoundMixer.bufferTime 值。要覆盖默认缓冲时间,请先创建一个新的 SoundLoaderContext 类实例,设置其 bufferTime 属性,然后将其作为参数传递给 Sound.load() 方法。
当回放继续进行时,Flash Player 尝试将声音缓冲保持在相同大小或更大。如果声音数据的加载速度比回放快,回放将继续进行而不会中断。但是,如果数据加载速率由于网络限制而减慢,播放头可能会到达声音缓冲区的结尾。如果发生这种情况,将暂停回放,但会在加载更多声音数据后自动恢复回放。
要查明暂停回放是否是由于 Flash Player 正在等待加载数据,请使用 Sound.isBuffering 属性。
播放声音
播放加载的声音非常简便,您只需为 Sound 对象调用 Sound.play() 方法。
使用 ActionScript 3.0 回放声音时,您可以执行以下操作:
从特定起始位置播放声音
暂停声音并稍后从相同位置恢复回放
准确了解何时播放完声音
跟踪声音的回放进度
在播放声音的同时更改音量或声相
要在回放期间执行这些操作,请使用 SoundChannel、SoundMixer 和 SoundTransform 类。
SoundChannel 类控制一种声音的回放。可以将 SoundChannel.position 属性视为播放头,以指示所播放的声音数据中的当前位置。
当应用程序调用 Sound.play() 方法时,将创建一个新的 SoundChannel 类实例来控制回放。
通过将特定起始位置(以毫秒为单位)作为 Sound.play() 方法的 startTime 参数进行传递,应用程序可以从该位置播放声音。它也可以通过在 Sound.play() 方法的 loops 参数中传递一个数值,指定快速且连续地将声音重复播放固定的次数。
暂停和恢复播放声音
如果应用程序播放很长的声音(如歌曲或播客),您可能需要让用户暂停和恢复回放这些声音。实际上,无法在 ActionScript 中的回放期间暂停声音;而只能将其停止。但是,可以从任何位置开始播放声音。您可以记录声音停止时的位置,并随后从该位置开始重放声音。
在播放声音的同时,SoundChannel.position 属性指示当前播放到的声音文件位置。应用程序可以在停止播放声音之前存储位置值,如下所示:
var pausePosition:int = channel.position;
channel.stop();
要恢复播放声音,请传递以前存储的位置值,以便从声音以前停止的相同位置重新启动声音。
channel = snd.play(pausePosition);
监视回放
应用程序可能需要了解何时停止播放某种声音,以便开始播放另一种声音,或者清除在以前回放期间使用的某些资源。SoundChannel 类在其声音完成播放时将调度 Event.SOUND_COMPLETE 事件。
SoundChannel 类在回放期间不调度进度事件。要报告回放进度,应用程序可以设置其自己的计时机制并跟踪声音播放头的位置。
要计算已播放的声音百分比,您可以将 SoundChannel.position 属性值除以所播放的声音数据长度:
var playbackPercent:uint = 100 * (channel.position / snd.length);
但是,仅当在开始回放之前完全加载了声音数据,此代码才会报告精确的回放百分比。Sound.length 属性显示当前加载的声音数据的大小,而不是整个声音文件的最终大小。要跟踪仍在加载的声音流的回放进度,应用程序应估计完整声音文件的最终大小,并在其计算中使用该值。您可以使用 Sound 对象的 bytesLoaded 和 bytesTotal 属性来估计声音数据的最终长度,如下所示:
var estimatedLength:int =
Math.ceil(snd.length / (snd.bytesLoaded / snd.bytesTotal));
var playbackPercent:uint = 100 * (channel.position / estimatedLength);
停止声音流
在进行流式传输的声音(即,在播放的同时仍在加载声音)的回放过程中,有一个奇怪的现象。当应用程序对回放声音流的 SoundChannel 实例调用 SoundChannel.stop() 方法时,声音回放在一个帧处停止,随后在下一帧处从声音开头重新回放。发生这种情况是因为,声音加载过程仍在进行当中。要停止声音流加载和回放,请调用 Sound.close() 方法。
加载和播放声音时的安全注意事项
可以根据 Flash Player 安全模型来限制应用程序访问声音数据的功能。每种声音受两种不同的安全沙箱的限制:内容本身的沙箱("内容沙箱")以及加载和播放声音的应用程序或对象的沙箱("所有者沙箱")。
内容沙箱控制使用 id3 属性还是 SoundMixer.computeSpectrum() 方法从声音提取详细声音数据。它不会限制声音文件本身的加载或播放。
声音文件的原始域定义了内容沙箱的安全限制。一般来说,如果某个声音文件与加载该文件的应用程序或对象的 SWF 文件位于相同的域或文件夹中,则应用程序或对象具有该声音文件的完全访问权限。如果声音来自与应用程序不同的域,仍可以使用跨域策略文件将其加载到内容沙箱中。
应用程序可以将带有 checkPolicyFile 属性的 SoundLoaderContext 对象作为参数传递给 Sound.load() 方法。如果将 checkPolicyFile 属性设置为 true,则会通知 Flash Player 在从中加载声音的服务器上查找跨域策略文件。如果存在跨域策略文件,并且它为执行加载的 SWF 文件所在的域授予了访问权限,则该 SWF 文件可以加载声音文件、访问 Sound 对象的 id3 属性以及为加载的声音调用 SoundMixer.computeSpectrum() 方法。
所有者沙箱控制声音的本地回放。所有者沙箱是由开始播放声音的应用程序或对象定义的。
只要当前播放的所有 SoundChannel 对象中的声音符合以下条件,SoundMixer.stopAll() 方法就会将它们静音:
声音是由相同所有者沙箱中的对象启动的。
声音来自具有跨域策略文件(为调用 SoundMixer.stopAll() 方法的应用程序或对象所在的域授予访问权限)的源。
要查明 SoundMixer.stopAll() 方法是否确实停止了所有播放的声音,应用程序可以调用 SoundMixer.areSoundsInaccessible() 方法。如果该方法返回值 true,则当前所有者沙箱无法控制播放的某些声音,SoundMixer.stopAll() 方法不会将其停止。
SoundMixer.stopAll() 方法还会阻止播放头继续播放从外部文件加载的所有声音。但是,如果动画移动到一个新帧,FLA 文件中嵌入的声音以及使用 Flash 创作工具附加到时间轴中的帧上的声音可能会重新开始播放。
控制音量和声相
单个 SoundChannel 对象控制声音的左和右立体声声道。如果 mp3 声音是单声道声音,SoundChannel 对象的左和右立体声声道将包含完全相同的波形。
可通过使用 SoundChannel 对象的 leftPeak 和 rightPeak 属性来查明所播放的声音的每个立体声声道的波幅。这些属性显示声音波形本身的峰值波幅。它们并不表示实际回放音量。实际回放音量是声音波形的波幅以及 SoundChannel 对象和 SoundMixer 类中设置的音量值的函数。
在回放期间,可以使用 SoundChannel 对象的 pan 属性为左和右声道分别指定不同的音量级别。pan 属性可以具有范围从 -1 到 1 的值,其中,-1 表示左声道以最大音量播放,而右声道处于静音状态;1 表示右声道以最大音量播放,而左声道处于静音状态。介于 -1 和 1 之间的数值为左和右声道值设置一定比例的值,值 0 表示两个声道以均衡的中音量级别播放。
可以在播放声音的同时更改音量和声相,方法是:设置 SoundTransform 对象的 pan 或 volume 属性,然后将该对象作为 SoundChannel 对象的 soundTransform 属性进行应用。
也可以通过使用 SoundMixer 类的 soundTransform 属性,同时为所有声音设置全局音量和声相值。
也可以使用 SoundTransform 对象为 Microphone 对象、Sprite 对象和 SimpleButton 对象设置音量和声相值。
处理声音元数据
使用 mp3 格式的声音文件可以采用 ID3 标签格式来包含有关声音的其它数据。
并非每个 mp3 文件都包含 ID3 元数据。当 Sound 对象加载 mp3 声音文件时,如果该声音文件包含 ID3 元数据,它将调度 Event.ID3 事件。要防止出现运行时错误,应用程序应等待接收 Event.ID3 事件后,再访问加载的声音的 Sound.id3 属性。
访问原始声音数据
通过使用 SoundMixer.computeSpectrum() 方法,应用程序可以读取当前所播放的波形的原始声音数据。如果当前播放多个 SoundChannel 对象,SoundMixer.computeSpectrum() 方法将显示混合在一起的每个 SoundChannel 对象的组合声音数据。
声音数据是作为 ByteArray 对象(包含 512 个字节的数据)返回的,其中的每个字节包含一个介于 -1 和 1 之间的浮点值。这些值表示所播放的声音波形中的点的波幅。这些值是分为两个组(每组包含 256 个值)提供的,第一个组用于左立体声声道,第二个组用于右立体声声道。
如果将 FFTMode 参数设置为 true,SoundMixer.computeSpectrum() 方法将返回频谱数据,而非波形数据。频谱显示按声音频率(从最低频率到最高频率)排列的波幅。可以使用快速傅立叶变换 (FFT) 将波形数据转换为频谱数据。生成的频谱值范围介于 0 和约 1.414(2 的平方根)之间。
computeSpectrum() 方法也可以返回已在较低比特率重新采样的数据。通常,这会产生更平滑的波形数据或频率数据,但会以牺牲细节为代价。stretchFactor 参数控制 computeSpectrum() 方法数据的采样率。如果将 stretchFactor 参数设置为 0(默认值),则以采样率 44.1 kHz 采集声音数据样本。stretchFactor 参数值每连续增加 1,采样率就减小一半,因此,值 1 指定采样率 22.05 kHz,值 2 指定采样率 11.025 kHz,依此类推。当使用较高的 stretchFactor 值时,computeSpectrum() 方法仍会为每个立体声声道返回 256 个字节。
SoundMixer.computeSpectrum() 方法具有一些限制:
由于来自麦克风或 RTMP 流的声音数据不是通过全局 SoundMixer 对象传递的,因此,SoundMixer.computeSpectrum() 方法不会从这些源返回数据。
如果播放的一种或多种声音来自当前内容沙箱以外的源,安全限制将导致 SoundMixer.computeSpectrum() 方法引发错误。
捕获声音输入
应用程序可通过 Microphone 类连接到用户系统上的麦克风或其它声音输入设备,并将输入音频广播到该系统的扬声器,或者将音频数据发送到远程服务器,如 Flash Media Server。
访问麦克风
Microphone 类没有构造函数方法。相反,应使用静态 Microphone.getMicrophone() 方法来获取新的 Microphone 实例。
不使用参数调用 Microphone.getMicrophone() 方法时,将返回在用户系统上发现的第一个声音输入设备。系统可能连接了多个声音输入设备。应用程序可以使用 Microphone.names 属性来获取所有可用声音输入设备名称的数组。然后,它可以使用 index 参数(与数组中的设备名称的索引值相匹配)来调用 Microphone.getMicrophone() 方法。系统可能没有连接麦克风或其它声音输入设备。可以使用 Microphone.names 属性或 Microphone.getMicrophone() 方法来检查用户是否安装了声音输入设备。如果用户未安装声音输入设备,则 names 数组的长度为零,并且 getMicrophone() 方法返回值 null。
当应用程序调用 Microphone.getMicrophone() 方法时,Flash Player 将显示"Flash Player 设置"对话框,它提示用户允许或拒绝 Flash Player 对系统上的摄像头和麦克风的访问。在用户单击此对话框中的"允许"或"拒绝"按钮后,将调度 StatusEvent。该 StatusEvent 实例的 code 属性指示是允许还是拒绝对麦克风的访问。如果允许访问,StatusEvent.code 属性将包含"Microphone.Unmuted";如果拒绝访问,则包含"Microphone.Muted"。
当用户允许或拒绝对麦克风的访问时,Microphone.muted 属性将被分别设置为 true 或 false。但是,在调度 StatusEvent 之前,不会在 Microphone 实例上设置 muted 属性,因此,应用程序还应等待调度 StatusEvent.STATUS 事件后再检查 Microphone.muted 属性。
将麦克风音频传送到本地扬声器
可以使用参数值 true 调用 Microphone.setLoopback() 方法,以将来自麦克风的音频输入传送到本地系统扬声器。
如果将来自本地麦克风的声音传送到本地扬声器,则会存在创建音频回馈循环的风险,这可能会导致非常大的振鸣声,并且可能会损坏声音硬件。使用参数值 true 调用 Microphone.setUseEchoSuppression() 方法可降低发生音频回馈的风险,但不会完全消除该风险。Adobe 建议您始终在调用 Microphone.setLoopback(true) 之前调用 Microphone.setUseEchoSuppression(true),除非您确信用户使用耳机来回放声音,或者使用除扬声器以外的某种设备。
更改麦克风音频
应用程序可以使用两种方法更改来自麦克风的音频数据。第一,它可以更改输入声音的增益,这会有效地将输入值乘以指定的数值以创建更大或更小的声音。Microphone.gain 属性接受介于 0 和 100 之间的数值(含 0 和 100)。值 50 相当于乘数 1,它指定正常音量。值 0 相当于乘数 0,它可有效地将输入音频静音。大于 50 的值指定的音量高于正常音量。
应用程序也可以更改输入音频的采样率。较高的采样率可提高声音品质,但它们也会创建更密集的数据流(使用更多的资源进行传输和存储)。Microphone.rate 属性表示以千赫 (kHz) 为单位测量的音频采样率。默认采样率是 8 kHz。如果麦克风支持较高的采样率,您可以将 Microphone.rate 属性设置为高于 8 kHz 的值。
检测麦克风活动
为节省带宽和处理资源,Flash Player 将尝试检测何时麦克风不传输声音。当麦克风的活动级别处于静音级别阈值以下一段时间后,Flash Player 将停止传输音频输入,并调度一个简单的 ActivityEvent。
Microphone 类的以下三个属性用于监视和控制活动检测:
activityLevel 只读属性指示麦克风检测的音量,范围从 0 到 100。
silenceLevel 属性指定激活麦克风并调度 ActivityEvent.ACTIVITY 事件所需的音量。silenceLevel 属性也使用从 0 到 100 的范围,默认值为 10。
silenceTimeout 属性描述活动级别处于静音级别以下多长时间(以毫秒为单位)后,才会调度 ActivityEvent.ACTIVITY 事件以指示麦克风现在处于静音状态。silenceTimeout 默认值是 2000。
Microphone.silenceLevel 属性和 Microphone.silenceTimeout 属性都是只读的,但可以使用 Microphone.setSilenceLevel() 方法来更改它们的值。
在某些情况下,在检测到新活动时激活麦克风的过程可能会导致短暂的延迟。通过将麦克风始终保持活动状态,可以消除此类激活延迟。应用程序可以调用 Microphone.setSilenceLevel() 方法并将 silenceLevel 参数设置为零,以通知 Flash Player 将麦克风保持活动状态并持续收集音频数据,即使未检测到任何声音也是如此。反之,如果将 silenceLevel 参数设置为 100,则可以完全禁止激活麦克风。
向媒体服务器发送音频以及从中接收音频
将 ActionScript 与 Flash Media Server 等流媒体服务器配合使用时,可以使用额外的音频功能。
特别地,应用程序可以将 Microphone 对象附加到 NetStream 对象上,并将数据直接从用户麦克风传输到服务器。也可以将音频数据从服务器流式传输到 Flash 或 Flex 应用程序,并将其作为 MovieClip 的一部分或使用 Video 对象进行回放。
捕获用户输入
用户输入基础知识
捕获用户输入简介
用户交互(无论是通过键盘、鼠标、摄像头还是这些设备的组合)是交互性的基础。在 ActionScript 3.0 中,识别和响应用户交互主要涉及事件侦听。
InteractiveObject 类是 DisplayObject 类的一个子类,它提供了处理用户交互所需的事件和功能的通用结构。您无法直接创建 InteractiveObject 类的实例。而是由显示对象(如 SimpleButton、Sprite、TextField 和各种 Flash 和 Flex 组件)从此类中继承其用户交互模型,因而它们使用同一个通用结构。这意味着,您为处理从 InteractiveObject 派生的一个对象中的用户交互而编写的代码以及学会的方法适用于所有其它对象。
重要概念和术语
在继续阅读本章内容之前,一定要先熟悉以下重要用户交互术语:
字符代码 (Character code):表示当前字符集中的字符(与在键盘上所按的键关联)的数字代码。例如,尽管"D"和"d"是由美国英语键盘上的相同键创建的,但它们具有不同的字符代码。
上下文菜单 (Context menu):当用户右键单击或使用特定键盘-鼠标组合时显示的菜单。上下文菜单命令通常直接应用于已单击的内容。
焦点 (Focus):指示选定元素是活动元素,并且它是键盘或鼠标交互的目标。
键控代码 (Key code):对应于键盘上的实际键的数字代码。
捕获键盘输入
从 InteractiveObject 类继承交互模型的显示对象可以使用事件侦听器来响应键盘事件。例如,您可以将事件侦听器放在舞台上以侦听并响应键盘输入。
有些键(如 Ctrl 键)虽然没有字型表示形式,也能生成事件。
在上面的代码示例中,键盘事件侦听器捕获了整个舞台的键盘输入。也可以为舞台上的特定显示对象编写事件侦听器;当对象具有焦点时将触发该事件侦听器。
了解键控代码和字符代码
您可以访问键盘事件的 keyCode 和 charCode 属性,以确定按下了哪个键,然后触发其它动作。keyCode 属性为数值,与键盘上的某个键的值相对应。charCode 属性是该键在当前字符集中的数值。(默认字符集是 UTF-8,它支持 ASCII。)
键控代码值与字符值之间的主要区别是键控代码值表示键盘上的特定键(数字小键盘上的 1 与最上面一排键中的 1 不同,但生成"1"的键与生成"!"的键是相同的),字符值表示特定字符(R 与 r 字符是不同的)。
键与其键控代码之间的映射取决于设备和操作系统。因此,不应使用键映射来触发动作,而应使用 Keyboard 类提供的预定义常量值来引用相应的 keyCode 属性。例如,不要使用 Shift 的键映射,而应使用 Keyboard.SHIFT 常量。
了解 KeyboardEvent 的优先顺序
与其它事件一样,键盘事件序列是由显示对象层次结构决定的,而不是由在代码中分配 addEventListener() 方法的顺序决定的。
操作系统和 Web 浏览器在 Adobe Flash Player 之前处理键盘事件。
捕获鼠标输入
鼠标单击将创建鼠标事件,这些事件可用来触发交互式功能。您可以将事件侦听器添加到舞台上以侦听在 SWF 文件中任何位置发生的鼠标事件。也可以将事件侦听器添加到舞台上从 InteractiveObject 进行继承的对象(例如,Sprite 或 MovieClip)中;单击该对象时将触发这些侦听器。
与键盘事件一样,鼠标事件也会冒泡。
MouseEvent 对象还包含 altKey、ctrlKey 和 shiftKey 布尔属性。可以使用这些属性来检查在鼠标单击时是否还按下了 Alt、Ctrl 或 Shift 键。
自定义鼠标光标
可以将鼠标光标(鼠标指针)隐藏或交换为舞台上的任何显示对象。要隐藏鼠标光标,请调用 Mouse.hide() 方法。可通过以下方式来自定义光标:调用 Mouse.hide(),侦听舞台上是否发生 MouseEvent.MOUSE_MOVE 事件,以及将显示对象(自定义光标)的坐标设置为事件的 stageX 和 stageY 属性。
自定义上下文菜单
从 InteractiveObject 类进行继承的每个对象可以具有唯一的上下文菜单,用户在 SWF 文件内右键单击时将显示该菜单。默认情况下,菜单中包含几个命令,其中包括"前进"、"后退"、"打印"、"品质"和"缩放"。
除了"设置"和"关于"命令外,您可以从菜单中删除所有其它默认命令。如果将 Stage 属性 showDefaultContextMenu 设置为 false,则会从上下文菜单中删除这些命令。
要为特定显示对象创建自定义的上下文菜单,请创建 ContextMenu 类的一个新实例,调用 hideBuiltInItems() 方法,并将该实例分配给该 DisplayObject 实例的 contextMenu 属性。
管理焦点
交互式对象可以按编程方式或通过用户动作来获得焦点。在这两种情况下,设置焦点会将对象的 focus 属性更改为 true。另外,如果将 tabEnabled 属性设置为 true,用户可通过按 Tab 将焦点从一个对象传递到另一个对象。请注意,默认情况下,tabEnabled 值为 false,但以下情况除外:
对于 SimpleButton 对象,该值为 true。
对于输入文本字段,该值为 true。
对于 buttonMode 设置为 true 的 Sprite 或 MovieClip 对象,该值为 true。
在上述各种情况下,都可以为 FocusEvent.FOCUS_IN 或 FocusEvent.FOCUS_OUT 添加侦听器,以便在焦点更改时提供其它行为。这对文本字段和表单尤其有用,但也可以用于 sprite、影片剪辑或从 InteractiveObject 类进行继承的任何对象。
网络与通信
网络和通信基础知识
网络和通信简介
当构建更复杂的 ActionScript 应用程序时,通常需要与服务器端脚本进行通信,或者从外部 XML 文件或文本文件加载数据。flash.net 包中包含用于通过 Internet 收发数据的类;例如,从远程 URL 加载内容、与其它 Flash Player 实例进行通信以及连接到远程网站。
而在 ActionScript 3.0,可以使用 URLLoader 和 URLRequest 类加载外部文件。可随后使用特定类来访问数据,具体取决于加载的数据类型。例如,如果将远程内容的格式设置为名称-值对,则可以使用 URLVariables 类来分析服务器结果。或者,如果使用 URLLoader 和 URLRequest 类加载的文件是远程 XML 文档,则可以使用 XML 类的构造函数、XMLDocument 类的构造函数或 XMLDocument.parseXML() 方法来分析 XML 文档。这样,您便可以简化 ActionScript 代码,因为无论是使用 URLVariables、XML 还是某个其它类来分析和处理远程数据,用于加载外部文件的代码都是相同的。
flash.net 包中还包含用于其它类型的远程通信的类。这些类包括 FileReference 类(用于将文件上载到服务器以及从服务器下载文件)、Socket 和 XMLSocket 类(用于通过套接字连接直接与远程计算机进行通信)以及 NetConnection 和 NetStream 类(用于与特定于 Flash 的服务器资源(如 Flash Media Server 和 Flash Remoting 服务器)进行通信以及加载视频文件)。
最后,flash.net 包中包含用于用户本地计算机上通信的类。这些类包括 LocalConnection 类(用于在一台计算机上运行的两个或多个 SWF 文件之间的通信)和 SharedObject 类(用于将数据存储在用户的计算机上,并在以后返回到应用程序时检索这些数据)。
重要概念和术语
外部数据 (External data):此类数据以某些形式存储在 SWF 文件外部,并在需要时加载到 SWF 文件中。可以将此数据存储在直接加载的文件中,将其存储在数据库中,或者以其它形式进行存储,以便通过调用在服务器上运行的脚本或程序对其进行检索。
URL 编码变量 (URL-encoded variable):URL 编码格式提供了一种在单个文本字符串中表示多个变量(变量名和值对)的方法。各个变量采用 name=value 格式。每个变量(即每个名称-值对)之间用"and"符隔开,如下所示:variable1=value1&variable2=value2。这样,便可以将不限数量的变量作为一条消息进行发送。
MIME 类型 (MIME type):用于在 Internet 通信中标识给定文件类型的标准代码。任何给定文件类型都具有用于对其进行标识的特定代码。发送文件或消息时,计算机(如 Web 服务器或用户的 Flash Player 实例)将指定要发送的文件类型。
HTTP:超文本传输协议,这是一种标准格式,用于传送通过 Internet 发送的网页和其它各种类型的内容。
请求方法 (Request method):当程序(如 Flash Player)或 Web 浏览器将消息(称为 HTTP 请求)发送到 Web 服务器时,可以使用以下两种方法之一将发送的任何数据嵌入到请求中:这两种方法是两个"请求方法",即 GET 和 POST。在服务器端,接收请求的程序需要查看相应的请求部分以查找数据,因此,用于从 ActionScript 发送数据的请求方法应与用于在服务器上读取该数据的请求方法相匹配。
套接字连接 (Socket connection):用于两台计算机之间的通信的永久连接。
上载 (Upload):将文件发送到另一台计算机。
下载 (Download):从另一台计算机检索文件。
处理外部数据
ActionScript 3.0 包含用于从外部源加载数据的机制。这些源可以是静态内容(如文本文件),也可以是动态内容(如从数据库检索数据的 Web 脚本)。可以使用多种不同的方法来设置数据的格式,并且 ActionScript 提供了用于解码和访问数据的功能。也可以在检索数据的过程中将数据发送到外部服务器。
使用 URLLoader 类和 URLVariables 类
ActionScript 3.0 使用 URLLoader 和 URLVariables 类来加载外部数据。URLLoader 类以文本、二进制数据或 URL 编码变量的形式从 URL 下载数据。URLLoader 类用于下载文本文件、XML 或其它要用于数据驱动的动态 ActionScript 应用程序中的信息。URLLoader 类使用 ActionScript 3.0 高级事件处理模型,使用该模型可以侦听诸如 complete、httpStatus、ioError、open、progress 和 securityError 等事件。
与早期版本 ActionScript 中的 XML 和 LoadVars 类非常相似,URLLoader URL 的数据在下载完成之前不可用。尽管如果文件加载速度太快,可能不会调度 ProgressEvent.PROGRESS 事件,但您可以通过侦听要调度的 flash.events.ProgressEvent.PROGRESS 事件来监视下载进度(已加载的字节数和总字节数)。成功下载文件后,将调度 flash.events.Event.COMPLETE 事件。加载的数据将从 UTF-8 或 UTF-16 编码被解码为字符串。
如果没有为 URLRequest.contentType 设置值,则以 application/x-www-form-urlencoded 的形式发送值。
URLLoader.load() 方法(以及 URLLoader 类的构造函数,可选)使用一个参数,即 request,该参数是一个 URLRequest 实例。URLRequest 实例包含单个 HTTP 请求的所有信息,如目标 URL、请求方法(GET 或 POST)、附加标头信息以及 MIME 类型(例如,当上载 XML 内容时)。
可以使用三种方式指定要在 URL 请求中传递的参数:
在 URLVariables 构造函数中
在 URLVariables.decode() 方法中
作为 URLVariables 对象本身中的特定属性
当定义 URLVariables 构造函数或 URLVariables.decode() 方法中的变量时,需要确保对"and"符进行 URL 编码,因为它具有特殊含义并作为分隔符使用。例如,由于与号作为参数的分隔符使用,当传递与号时,需要将与号从 & 更改为 %26 来对与号进行 URL 编码。
从外部文档加载数据
当使用 ActionScript 3.0 生成动态应用程序时,最好从外部文件或服务器端脚本加载数据。这样,您不必编辑或重新编译 ActionScript 文件,即可生成动态应用程序。
默认情况下,如果您未定义请求方法,Flash Player 将使用 HTTP GET 方法加载内容。如果要使用 POST 方法发送数据,则需要使用静态常量 URLRequestMethod.POST 将 request.method 属性设置为 POST。
不要将保留字或语言构造作为外部数据文件中的变量名称,因为这样做会使代码的读取和调试变得更困难。
加载数据后,将调度 Event.COMPLETE 事件,现可以在 URLLoader 的 data 属性中使用外部文档的内容。
如果远程文档包含名称-值对,您可以通过传入加载文件的内容,使用 URLVariables 类来分析数据。
外部文件中的每个名称-值对都创建为 URLVariables 对象中的一个属性。
如果从外部文本文件加载数值数据,则需要使用顶级函数(如 int()、uint() 或 Number())将这些值转换为数值。
无需将远程文件的内容作为字符串加载和新建 URLVariables 对象,您可以将 URLLoader.dataFormat 属性设置为在 URLLoaderDataFormat 类中找到的静态属性之一。URLLoader.dataFormat 属性的三个可能值如下:
URLLoaderDataFormat.BINARY ─ URLLoader.data 属性将包含 ByteArray 对象中存储的二进制数据。
URLLoaderDataFormat.TEXT ─ URLLoader.data 属性将包含 String 对象中的文本。
URLLoaderDataFormat.VARIABLES ─ URLLoader.data 属性将包含 URLVariables 对象中存储的 URL 编码的变量。
URLLoader.dataFormat 的默认值为 URLLoaderDataFormat.TEXT。
从外部文件加载 XML 与加载 URLVariables 相同。可以创建 URLRequest 和 URLLoader 实例,然后使用它们下载远程 XML 文档。文件完全下载后,调度 Event.COMPLETE 事件,并将外部文件的内容转换为可使用 XML 方法和属性分析的 XML 实例。
与外部脚本进行通信
除了加载外部数据文件,还可以使用 URLVariables 类将变量发送到服务器端脚本并处理服务器的响应。
连接到其它 Flash Player 实例
通过使用 LocalConnection 类,可以在不同的 Flash Player 实例(例如 HTML 容器、嵌入或独立播放器中的 SWF)之间进行通信。这样,您便可以构建在 Flash Player 实例之间共享数据(例如在 Web 浏览器中运行或嵌入在桌面应用程序中的 SWF 文件)的各种不同的应用程序。
LocalConnection 类
LocalConnection 类用于开发 SWF 文件,这些文件无需使用 fscommand() 方法或 JavaScript 即可向其它 SWF 文件发送指令。LocalConnection 对象只能在同一客户端计算机上运行的 SWF 文件间进行通信,但是它们可以在不同的应用程序中运行。
可以使用 LocalConnection 对象在使用不同 ActionScript 版本的 SWF 之间进行通信:
ActionScript 3.0 LocalConnection 对象可以与使用 ActionScript 1.0 或 2.0 创建的 LocalConnection 对象进行通信。
ActionScript 1.0 或 2.0 LocalConnection 对象可以与使用 ActionScript 3.0 创建的 LocalConnection 对象进行通信。
Flash Player 可自动处理不同版本 LocalConnection 对象间的通信。
最简便的 LocalConnection 对象使用方法是只允许位于同一个域中的 LocalConnection 对象之间进行通信。这样,您就不必担心安全方面的问题。
可以使用 LocalConnection 对象在一个 SWF 文件中收发数据,但是 Adobe 不建议这样做。相反,您应使用共享对象。
可以使用三种方式将回调方法添加到 LocalConnection 对象中:
使 LocalConnection 类成为子类,并添加方法。
将 LocalConnection.client 属性设置为实现方法的对象。
创建扩展 LocalConnection 的动态类,并动态附加方法。
添加回调方法的第一种方式是扩展 LocalConnection 类。您在自定义类中定义方法,而不是将它们动态添加到 LocalConnection 实例中。
添加回调方法的第二种方式是使用 LocalConnection.client 属性。这包括创建自定义类和将新实例分配给 client 属性,LocalConnection.client 属性指示应调用的对象回调方法。
添加回调方法的第三种方式是创建动态类并动态附加该方法,这与在早期版本的 ActionScript 中使用 LocalConnection 类非常相似,不建议使用上面这种添加回调方法的方式,因为该代码不是非常易于移植。
在两个 Flash Player 实例之间发送消息
可以使用 LocalConnection 类在 Flash Player 的不同实例之间进行通信。例如,可以在网页上有多个 Flash Player 实例,或者让 Flash Player 实例从弹出窗口中的 Flash Player 实例检索数据。
下面的代码定义一个本地连接对象,该对象作为服务器使用,接受来自其它 Flash Player 实例的传入调用:
package
{
import flash.net.LocalConnection;
import flash.display.Sprite;
public class ServerLC extends Sprite
{
public function ServerLC()
{
var lc:LocalConnection = new LocalConnection();
lc.client = new CustomClient1();
try
{
lc.connect("conn1");
}
catch (error:Error)
{
trace("error:: already connected");
}
}
}
}
此代码先创建一个名为 lc 的 LocalConnection 对象,然后将 client 属性设置为自定义类 CustomClient1。当另一个 Flash Player 实例调用此本地连接实例中的某方法时,Flash Player 在 CustomClient1 类中查找该方法。
当 Flash Player 实例连接到此 SWF 文件并尝试调用指定本地连接的任何方法时,系统会将请求发送到 client 属性指定的类(该属性被设置为 CustomClient1 类):
package
{
import flash.events.*;
import flash.system.fscommand;
import flash.utils.Timer;
public class CustomClient1 extends Object
{
public function doMessage(value:String = ""):void
{
trace(value);
}
public function doQuit():void
{
trace("quitting in 5 seconds");
this.close();
var quitTimer:Timer = new Timer(5000, 1);
quitTimer.addEventListener(TimerEvent.TIMER, closeHandler);
}
public function closeHandler(event:TimerEvent):void
{
fscommand("quit");
}
}
}
要创建 LocalConnection 服务器,请调用 LocalConnection.connect() 方法并提供唯一的连接名称。如果已存在具有指定名称的连接,则会生成 ArgumentError 错误,指出由于已经连接了该对象,连接尝试失败。
下面的片断说明如何使用名称 conn1 创建新的套接字连接:
try
{
connection.connect("conn1");
}
catch (error:ArgumentError)
{
trace("Error! Server already existsn");
}
在早期版本的 ActionScript 中,如果连接名称已被使用,LocalConnection.connect() 方法则会返回一个布尔值。在 ActionScript 3.0 中,如果该名称已被使用,则生成错误。
从辅助 SWF 文件连接到主 SWF 文件需要在发送方 LocalConnection 对象中新建 LocalConnection 对象,然后使用连接名称和要执行的方法名称来调用 LocalConnection.send() 方法。例如,要连接到早先创建的 LocalConnection 对象,可以使用下面的代码:
sendingConnection.send("conn1", "doQuit");
此代码使用连接名称 conn1 连接到现有 LocalConnection 对象,并调用远程 SWF 文件中的 doQuit() 方法。如果想要将参数发送到远程 SWF 文件,可以在 send() 方法中的方法名称后指定附加参数,如下面的片断所示:
sendingConnection.send("conn1", "doMessage", "Hello world");
连接到不同域中的 SWF 文档
要只允许从特定域进行通信,可以调用 LocalConnection 类的 allowDomain() 或 allowInsecureDomain() 方法,并传递包含允许访问此 LocalConnection 对象的一个或多个域的列表。
在早期版本的 ActionScript 中,LocalConnection.allowDomain() 和 LocalConnection.allowInsecureDomain() 是必须由开发人员实现的、且必须返回布尔值的回调方法。在 ActionScript 3.0 中,LocalConnection.allowDomain() 和 LocalConnection.allowInsecureDomain() 都是内置方法,开发人员可以像调用 Security.allowDomain() 和 Security.allowInsecureDomain() 那样调用这两个内置方法,传递要允许的一个或多个域的名称。
可以向 LocalConnection.allowDomain() 和 LocalConnection.allowInsecureDomain() 方法传递两个特殊值:* 和 localhost。星号值 (*) 表示允许从所有域访问。字符串 localhost 允许从本地安装的 SWF 文件调用 SWF 文件。
Flash Player 8 对本地 SWF 文件引入了安全限制。可以访问 Internet 的 SWF 文件还不能访问本地文件系统。如果指定 localhost,则任何本地 SWF 文件都可以访问 SWF 文件。如果 LocalConnection.send() 方法试图从调用代码没有访问权限的安全沙箱与 SWF 文件进行通信,则会调度 securityError 事件 (SecurityErrorEvent.SECURITY_ERROR)。要解决此错误,可以在接收方的 LocalConnection.allowDomain() 方法中指定调用方的域。
如果仅在同一个域中的 SWF 文件之间实现通信,可以指定一个不以下划线 (_) 开头且不指定域名的 connectionName 参数(例如 myDomain:connectionName)。在 LocalConnection.connect(connectionName) 命令中使用相同的字符串。
如果要实现不同域中的 SWF 文件之间的通信,可以指定一个以下划线开头的 connectionName 参数。指定下划线使具有接收方 LocalConnection 对象的 SWF 文件更易于在域之间移植。下面是两种可能的情形:
如果 connectionName 字符串不以下划线开头,则 Flash Player 会添加一个包含超级域名称和一个冒号的前缀(例如 myDomain:connectionName)。虽然这可以确保您的连接不会与其它域中具有同一名称的连接冲突,但任何发送方 LocalConnection 对象都必须指定此超级域(例如 myDomain:connectionName)。如果将具有接收方 LocalConnection 对象的 SWF 文件移动到另一个域中,Flash Player 会更改前缀,以反映新的超级域(例如 anotherDomain:connectionName)。必须手动编辑所有发送方 LocalConnection 对象,以指向新超级域。
如果 connectionName 字符串以下划线开头(例如 _connectionName),Flash Player 不会向该字符串添加前缀。这意味着接收方和发送方 LocalConnection 对象都将使用相同的 connectionName 字符串。如果接收方对象使用 LocalConnection.allowDomain() 来指定可以接受来自任何域的连接,则可以将具有接收方 LocalConnection 对象的 SWF 文件移动到另一个域,而无需更改任何发送方 LocalConnection 对象。
套接字连接
在 ActionScript 3.0 中,可以使用两种不同类型的套接字连接:XML 套接字连接和二进制套接字连接。使用 XML 套接字,可以连接到远程服务器并创建服务器连接,该连接在显式关闭之前一直保持打开。这样,无需不断打开新服务器连接,就可以在服务器与客户端之间交换 XML 数据。使用 XML 套接字服务器的另一个好处是用户不需要显式请求数据。您无需请求即可从服务器发送数据,并且可以将数据发送到连接到 XML 套接字服务器的每个客户端。
二进制套接字连接与 XML 套接字类似,不同之处是客户端与服务器不需要专门交换 XML 数据包,连接可以将数据作为二进制信息传输。这样,您就可以连接到各种各样的服务,包括邮件服务器(POP3、SMTP 和 IMAP)和新闻服务器 (NNTP)。
Socket 类
ActionScript 3.0 中引入的 Socket 类使 ActionScript 可以建立套接字连接并读取和写入原始二进制数据。它与 XMLSocket 类相似,但没有指定接收和传输数据的格式。使用二进制协议的服务器互操作时,Socket 类与非常有用。使用二进制套接字连接,可以编写允许用一些不同的 Internet 协议(例如 POP3、SMTP、IMAP 和 NNTP)进行交互的代码。反过来,这又会使 Flash Player 能够连接到邮件和新闻服务器。
Flash Player 可通过使用服务器的二进制协议直接与该服务器连接。某些服务器使用 big-endian 字节顺序,某些服务器则使用 little-endian 字节顺序。Internet 上的大多数服务器使用 big-endian 字节顺序,因为"网络字节顺序"为 big-endian。little-endian 字节顺序很常用,因为 Intel x86® 体系结构使用该字节顺序。您应使用与收发数据的服务器的字节顺序相匹配的 endian 字节顺序。默认情况下,IDataInput 和 IDataOutput 接口执行的所有操作和实现这些接口的类(ByteArray、Socket 和 URLStream)都以 big-endian 格式编码;即,最高有效字节位于前面。这样做是为了匹配 Java 和官方网络字节顺序。要更改是使用 big-endian 还是使用 little-endian 字节顺序,可以将 endian 属性设置为 Endian.BIG_ENDIAN 或 Endian.LITTLE_ENDIAN。
Socket 类继承 IDataInput 和 IDataOutput 接口(位于 flash.utils 包中)实现的所有方法,应使用这些方法从 Socket 读取数据和向其中写入数据。
XMLSocket 类
ActionScript 提供了一个内置的 XMLSocket 类,使用它可以打开与服务器的持续连接。这种打开的连接消除了反应时间问题,它通常用于实时的应用程序,例如聊天应用程序或多人游戏。传统的基于 HTTP 的聊天解决方案频繁轮询服务器,并使用 HTTP 请求来下载新的消息。与此相对照,XMLSocket 聊天解决方案保持与服务器的开放连接,这一连接允许服务器即时发送传入的消息,而无需客户端发出请求。
要创建套接字连接,必须创建服务器端应用程序来等待套接字连接请求,然后向 SWF 文件发送响应。可以使用 Java、Python 或 Perl 程语言来编写这种类型的服务器端应用程序。要使用 XMLSocket 类,服务器计算机必须运行可识别 XMLSocket 类使用的协议的守护程序。下面的列表说明了该协议:
通过全双工 TCP/IP 流套接字连接发送 XML 消息。
每个 XML 消息都是一个完整的 XML 文档,以一个零 (0) 字节结束。
通过一个 XMLSocket 连接发送和接收的 XML 消息的数量没有限制。
XMLSocket 类不能自动穿过防火墙,因为 XMLSocket 没有 HTTP 隧道功能(这与实时消息传递协议 (RTMP) 不同)。如果您需要使用 HTTP 隧道,应考虑改用 Flash Remoting 或 Flash Media Server(支持 RTMP)。
XMLSocket 对象连接到服务器的方式和位置受以下限制:
XMLSocket.connect() 方法只能连接到端口号大于或等于 1024 的 TCP 端口。这种限制的一个后果是,与 XMLSocket 对象进行通信的服务器守护程序也必须分配到端口号大于或等于 1024 的端口。端口号小于 1024 的端口通常用于系统服务(如 FTP (21)、Telnet (23)、SMTP (25)、HTTP (80) 和 POP3 (110)),因此,出于安全方面的考虑,禁止 XMLSocket 对象使用这些端口。这种端口号方面的限制可以减少不恰当地访问和滥用这些资源的可能性。
XMLSocket.connect() 方法只能连接到 SWF 文件所在域中的计算机。这一限制不适用于在本地磁盘外运行的 SWF 文件。(此限制与 URLLoader.load() 的安全规则相同。)要连接到在 SWF 所在域之外的其它域中运行的服务器守护程序,可以在该服务器上创建一个允许从特定域进行访问的安全策略文件。
将服务器设置为与 XMLSocket 对象进行通信可能会遇到一些困难。如果您的应用程序不需要进行实时交互,请使用 URLLoader 类,而不要使用 XMLSocket 类。
可以使用 XMLSocket 类的 XMLSocket.connect() 和 XMLSocket.send() 方法,通过套接字连接与服务器之间传输 XML。XMLSocket.connect() 方法与 Web 服务器端口建立套接字连接。XMLSocket.send() 方法将 XML 对象传递给套接字连接中指定的服务器。
调用 XMLSocket.connect() 方法时,Flash Player 打开与服务器的 TCP/IP 连接,并使该连接保持打开状态,直到发生以下任一事件:
XMLSocket 类的 XMLSocket.close() 方法被调用。
对 XMLSocket 对象的引用不再存在。
Flash Player 退出。
连接中断(例如,调制解调器断开连接)。
存储本地数据
共享对象(有时称为"Flash cookie")是一个数据文件,您访问的站点可能会在您的计算机上创建该文件。共享对象通常用于增强您的 Web 浏览体验 ─ 例如,使用它可以个性化经常访问的网站的外观。共享对象本身不能对您计算机上的数据进行任何操作,也不能使用这些数据进行任何操作。更重要的是,共享对象永远不能访问或记住您的电子邮件地址或其它个人信息 ─ 除非您愿意提供这样的信息。
可以使用静态的 SharedObject.getLocal() 或 SharedObject.getRemote() 方法来创建新的共享对象实例。getLocal() 方法尝试加载仅对当前客户端可用的本地永久共享对象,而 getRemote() 方法则尝试加载可借助服务器(例如 Flash Media Server)跨多个客户端共享的远程共享对象。如果本地或远程共享对象不存在,则 getLocal() 和 getRemote() 方法将创建一个新的 SharedObject 实例。
可以使用 SharedObject.size 属性来确定共享对象是否已存在。
为了将共享对象保存到用户的硬盘驱动器,您必须显式地调用 SharedObject.flush() 方法。使用 flush() 方法将共享对象写入用户的硬盘时,应仔细检查用户是否已使用 Flash Player 设置管理器显式禁用了本地存储。
显示共享对象的内容
值存储在共享对象中的 data 属性中。
创建安全 SharedObject
当使用 getLocal() 或 getRemote() 创建本地或远程 SharedObject 时,有一个名为 secure 的可选参数,该参数确定对此共享对象的访问是否限于通过 HTTPS 连接传递的 SWF 文件。如果此参数设置为 true 且 SWF 文件是通过 HTTPS 传递的,Flash Player 将新建一个安全共享对象,或者获取对现有安全共享对象的引用。此安全共享对象只能由通过 HTTPS 传递的 SWF 文件来读取或写入,SWF 文件将调用 SharedObject.getLocal() 并将 secure 参数设置为 true。如果此参数设置为 false 且 SWF 文件是通过 HTTPS 传递的,Flash Player 将新建一个共享对象,或者获取对现有共享对象的引用。
还可由通过非 HTTPS 连接传递的 SWF 文件对此共享对象进行读取或写入。如果 SWF 文件是通过非 HTTPS 连接传递的,并且您尝试将此参数设置为 true,将无法创建新的共享对象(或访问以前创建的安全共享对象),并会引发错误,并且共享对象设置为 null。
处理文件上载和下载
可通过使用 FileReference 类,在客户端和服务器之间添加上载和下载文件的功能。将通过一个对话框(例如,Windows 操作系统中的"打开"对话框)提示用户选择要上载的文件或用于下载的位置。
使用 ActionScript 创建的每个 FileReference 对象都引用用户硬盘上的一个文件。该对象的属性包含有关文件大小、类型、名称、创建日期和修改日期的信息。
仅 Mac OS 支持 creator 属性。所有其它平台都会返回 null。
可以通过两种方式创建 FileReference 类的实例。可以使用 new 运算符,如以下代码所示:
import flash.net.FileReference;
var myFileReference:FileReference = new FileReference();
或者,可以调用 FileReferenceList.browse() 方法,该方法在用户的系统中打开一个对话框,提示用户选择一个或多个要上载的文件,如果用户成功选择了一个或多个文件,将创建一个由 FileReference 对象组成的数组。每个 FileReference 对象表示用户在对话框中选择的一个文件。FileReference 对象在 FileReference 属性(例如 name、size 或 modificationDate)中不包含任何数据,直到发生下列任一情况:
已调用 FileReference.browse() 方法或 FileReferenceList.browse() 方法,并且用户已经从文件选取器中选择了文件。
已调用 FileReference.download() 方法,并且用户已经从文件选取器中选择了文件。
当执行下载时,在下载完成前只填充 FileReference.name 属性。下载完文件后,所有属性都将可用。在执行对 FileReference.browse()、FileReferenceList.browse() 或 FileReference.download() 方法的调用时,大多数播放器将继续 SWF 文件播放。
FileReference 类
使用 FileReference 类可以在用户的计算机和服务器之间上载和下载文件。操作系统对话框会提示用户选择要上载的文件或用于下载的位置。每个 FileReference 对象都引用用户磁盘上的一个文件并且具有一些属性,这些属性包含有关文件大小、类型、名称、创建日期、修改日期以及创建者的信息。
FileReference 实例的创建方法有两种:
使用 new 运算符和 FileReference 构造函数,如下所示:
var myFileReference:FileReference = new FileReference();
调用 FileReferenceList.browse(),从而创建 FileReference 对象数组。
对于上载和下载操作,SWF 文件只能访问自己的域(包括由跨域策略文件指定的任何域)内的文件。如果启动上载或下载的 SWF 与文件服务器不在同一个域中,则需要将策略文件放到文件服务器上。
一次只能执行一个 browse() 或 download() 操作,因为在任何时刻只能打开一个对话框。
处理文件上载的服务器脚本应收到包含下列元素的 HTTP POST 请求:
Content-Type,其值为 multipart/form-data。
Content-Disposition,其 name 属性设置为"Filedata",filename 属性设置为原始文件的名称。您可以通过在 FileReference.upload() 方法中传递 uploadDataFieldName 参数的值来指定自定义 name 属性。
文件的二进制内容。
将文件上载到服务器
要将文件上载到服务器,需要首先调用 browse() 方法,以允许用户选择一个或多个文件。接下来,当调用 FileReference.upload() 方法时,所选的文件将传输到服务器。如果用户使用 FileReferenceList.browse() 方法选择了多个文件,Flash Player 将创建一个称为 FileReferenceList.fileList 的所选文件数组。可随后使用 FileReference.upload() 方法分别上载每个文件。
使用 FileReference.browse() 方法时,您只能上载单个文件。要允许用户上载多个文件,您必须使用 FileReferenceList.browse() 方法。
虽然开发人员可以通过使用 FileFilter 类并将文件过滤器实例数组传递给 browse() 方法来指定一个或多个自定义文件类型过滤器,但是默认情况下,系统文件选取器对话框允许用户从本地计算机选取任何文件类型。
您可以使用 URLRequest.method 和 URLRequest.data 属性,通过 FileReference.upload() 方法将数据发送到服务器,以使用 POST 或 GET 方法发送变量。
当您尝试使用 FileReference.upload() 方法上载文件时,可能调度下列任何事件:
Event.OPEN:上载操作开始时调度。
ProgressEvent.PROGRESS:文件上载操作期间定期调度。
Event.COMPLETE:文件上载操作成功完成时调度。
SecurityErrorEvent.SECURITY_ERROR:由于安全侵犯导致上载失败时调度。
HTTPStatusEvent.HTTP_STATUS:由于 HTTP 错误导致上载失败时调度。
IOErrorEvent.IO_ERROR:由于下列任何原因导致上载失败时调度:
当 Flash Player 正在读取、写入或传输文件时发生输入/输出错误。
SWF 尝试将文件上载到要求身份验证(如用户名和密码)的服务器。在上载期间,Flash Player 不为用户提供输入密码的方法。
url 参数包含无效协议。FileReference.upload() 方法必须使用 HTTP 或 HTTPS。
Flash Player 不对需要身份验证的服务器提供完全支持。只有使用浏览器插件或 Microsoft ActiveX® 控件在浏览器中运行的 SWF 文件才可以提供一个对话框,来提示用户输入用户名和密码以进行身份验证,并且用户只有在通过身份验证后才能下载。对于使用插件或 ActiveX 控件进行的上载操作,或者使用独立或外部播放器进行的上载/下载操作,文件传输会失败。
在 ActionScript 3.0 中,可以使用 URLRequest 对象将变量传递到远程脚本,该对象允许您使用 POST 或 GET 方法传递数据;因此,可以更轻松和更清晰地传递较大数据集。为了指定是使用 GET 还是使用 POST 请求方法来传递变量,可以将 URLRequest.method 属性相应设置为 URLRequestMethod.GET 或 URLRequestMethod.POST。
在 ActionScript 3.0 中,还可以通过向 upload() 方法提供第二个参数来覆盖默认 Filedata 上载文件字段名称,如上面的示例所示(该示例使用 Custom1 替换默认值 Filedata)。
默认情况下,Flash Player 不尝试发送测试上载,虽然您可以通过将值 true 作为第三个参数传递给 upload() 方法来覆盖此行为。测试上载的目的是检查实际文件上载是否会成功,如果需要服务器身份,还会检查服务器身份验证是否会成功。
目前,只在基于 Windows 的 Flash Player 上进行测试上载。
从服务器下载文件
您可以让用户使用 FileReference.download() 方法从服务器下载文件,该方法使用两个参数:request 和 defaultFileName。第一个参数是 URLRequest 对象,该对象包含要下载的文件的 URL。第二个参数是可选的,它允许您指定出现在下载文件对话框中的默认文件名。如果省略第二个参数 defaultFileName,则使用指定 URL 中的文件名。
FileReferenceList 类
使用 FileReferenceList 类,用户可以选择一个或多个要上载到服务器端脚本的文件。文件上载是由 FileReference.upload() 方法处理的,必须对用户选择的每个文件调用此方法。
客户端系统环境
客户端系统环境基础知识
客户端系统环境简介
在构建更高级的 ActionScript 应用程序时,您可能会发现需要了解有关用户操作系统的详细信息(和访问操作系统功能)。客户端系统环境是 flash.system 包中的类集合,可通过这些类来访问系统级功能,例如:
确定执行 SWF 时所在的应用程序和安全域
确定用户的 Flash Player 的功能,如屏幕大小(分辨率);以及确定某项功能是否可用,如 mp3 音频
使用 IME 建立多语言站点
与 Flash Player 的容器(可能是 HTML 页或容器应用程序)进行交互
将信息保存到用户的剪贴板中
flash.system 包还包括 IMEConversionMode 和 SecurityPanel 类。这两个类分别包含与 IME 和 Security 类一起使用的静态常数。
重要概念和术语
操作系统 (Operating system):计算机上运行的主程序(其它所有应用程序均运行在其中),如 Microsoft Windows、Mac OS X 或 Linux®。
剪贴板 (Clipboard):用于保存复制或剪切的文本或项目的操作系统容器,可从中将项目粘贴到应用程序中。
应用程序域 (Application domain):用于将不同 SWF 文件中使用的类分开的机制,以便在 SWF 文件包含具有相同名称的不同类时,这些类不会彼此覆盖。
IME(input method editor,输入法编辑器):用于通过标准键盘输入复杂字符或符号的程序(或操作系统工具)。
客户端系统:在编程术语中,"客户端"是指在单独计算机上运行并由单个用户使用的应用程序部分(或整个应用程序)。"客户端系统"是指用户计算机上的基础操作系统。
使用 System 类
System 类包含的一些方法和属性可让您与用户的操作系统进行交互,并检索 Adobe Flash Player 的当前内存使用数据。System 类的方法和属性还可用来侦听 imeComposition 事件,指示 Flash Player 使用用户的当前代码页加载外部文本文件或按 Unicode 进行加载,或者设置用户剪贴板的内容。
在运行时获取有关用户系统的数据
通过检查 System.totalMemory 属性,可以确定 Flash Player 当前所用的内存数量(以字节为单位)。该属性可让您监视内存使用情况,并根据内存级别的更改方式优化应用程序。
System.ime 属性是对当前安装的输入法编辑器 (IME) 的引用。该属性允许使用 addEventListener() 方法来侦听 imeComposition 事件 (flash.events.IMEEvent.IME_COMPOSITION)。
System 类中的第三个属性是 useCodePage。如果将 useCodePage 设置为 true,Flash Player 将使用运行播放器的操作系统的传统代码页来加载外部文本文件。如果将此属性设置为 false,则 Flash Player 按 Unicode 解释外部文件。
如果将 System.useCodePage 设置为 true,请记住,运行播放器的操作系统的传统代码页中必须包括在外部文本文件中使用的字符,这样才能显示文本。例如,如果您加载了一个包含中文字符的外部文本文件,则这些字符不能显示在使用英文 Windows 代码页的系统上,因为该代码页不包括中文字符。
要确保所有平台上的用户都能查看 SWF 文件中使用的外部文本文件,应将所有外部文本文件按 Unicode 进行编码,并将 System.useCodePage 设置保留为默认设置 false。这样,Flash Player 就会将文本解释为 Unicode。
将文本保存到剪贴板
System 类包含一个名为 setClipboard() 的方法,它允许 Flash Player 使用指定的字符串来设置用户剪贴板的内容。出于安全方面的考虑,不存在 Security.getClipboard() 方法,因为此类方法可能允许恶意站点访问最近复制到用户剪贴板中的数据。
使用 Capabilities 类
开发人员可通过 Capabilities 类来确定正在运行 SWF 文件的环境。使用 Capabilities 类的各种属性,可以查明用户系统的分辨率、用户的系统是否支持辅助功能软件、用户操作系统的语言以及当前安装的 Flash Player 的版本。
通过检查 Capabilities 类的属性,可以自定义应用程序,使其与特定用户环境更好地配合使用。例如,通过检查 Capabilities.screenResolutionX 和 Capabilities.screenResolutionY 属性,可以确定用户系统所使用的显示分辨率以及决定最合适的视频大小。或者,在尝试加载外部 mp3 文件之前,可以检查 Capabilities.hasMP3 属性以查看用户系统是否支持 mp3 回放。
使用 ApplicationDomain 类
ApplicationDomain 类的用途是存储 ActionScript 3.0 定义表。SWF 文件中的所有代码被定义为存在于应用程序域中。 可以使用应用程序域划分位于同一个安全域中的类。这允许同一个类存在多个定义,并且还允许子级重用父级定义。
在使用 Loader 类 API 加载用 ActionScript 3.0 编写的外部 SWF 文件时,可以使用应用程序域。(请注意,在加载图像或用 ActionScript 1.0 或 ActionScript 2.0 编写的 SWF 文件时不能使用应用程序域。)包含在已加载类中的所有 ActionScript 3.0 定义都存储在应用程序域中。加载 SWF 文件时,通过将 LoaderContext 对象的 applicationDomain 参数设置为 ApplicationDomain.currentDomain,可以指定文件包含在 Loader 对象所在的相同应用程序域中。通过将加载的 SWF 文件放在同一个应用程序域中,可以直接访问它的类。如果加载的 SWF 文件包含嵌入的媒体(可通过其关联的类名称访问),或者您要访问加载的 SWF 文件的方法,则这种方式会很有用。
使用应用程序域时,还要记住以下几点:
SWF 文件中的所有代码被定义为存在于应用程序域中。 主应用程序在“当前域”中运行。“系统域”中包含所有应用程序域(包括当前域),也就是,它包含所有 Flash Player 类。
所有应用程序域(除系统域外)都有关联的父域。主应用程序的应用程序域的父域是系统域。已加载的类仅在其父级中没有相关定义时才进行定义。不能用较新的定义覆盖已加载类的定义。
下图显示了某个应用程序在单个域 (domain1.com) 中加载多个 SWF 文件的内容。根据加载内容的不同,可以使用不同的应用程序域。紧跟的文本说明用于为应用程序中的每个 SWF 文件设置适当应用程序域的逻辑。


主应用程序文件为 application1.swf。它包含从其它 SWF 文件加载内容的 Loader 对象。在此方案下,当前域为 Application domain 1。用法 A、用法 B 和用法 C 说明了为应用程序中的每个 SWF 文件设置适当应用程序域的不同方法。
用法 A:通过创建系统域的子级划分子级 SWF 文件。在示意图中,Application domain 2 创建为系统域的子级。application2.swf 文件在 Application domain 2 中加载,因此其类定义从 application1.swf 中定义的类中划分出来。
此方法的一个用处是使旧版应用程序能够动态加载相同应用程序的更新版本,而不会发生冲突。之所以不发生冲突,是因为尽管使用的是同样的类名称,但它们划分到不同的应用程序域中。
以下代码将创建作为系统域子级的应用程序域:
request.url = "application2.swf";
request.applicationDomain = new ApplicationDomain();
用法 B:在当前类定义中添加新的类定义。module1.swf 的应用程序域设置为当前域 (Application domain 1)。这可让您将新的类定义添加到应用程序的当前一组类定义中。这可用于主应用程序的运行时共享库。加载的 SWF 被视为远程共享库 (RSL)。使用此方法可以在应用程序启动之前使用预加载器加载 RSL。
以下代码将某应用程序域设置为当前域:
request.url = "module1.swf";
request.applicationDomain = ApplicationDomain.currentDomain;
用法 C:通过创建当前域的新子域,使用父级的类定义。module3.swf 的应用程序域是当前域的子级,并且子级使用所有类的父级的版本。此方法的一个用处可能是作为一个使用主应用程序的类型的多屏幕丰富 Internet 应用程序 (RIA) 模块,该模块作为主应用程序的子级加载。如果能够确保所有类始终更新为向后兼容,并且正在加载的应用程序始终比其加载的软件的版本新,则子级将使用父级版本。如果可以确保不继续拥有对子级 SWF 的引用,则拥有了新的应用程序域还使您能够卸载所有的类定义以便于垃圾回收。
此方法使加载的模块可以共享加载者的 singleton 对象和静态类成员。
以下代码将创建当前域的新子域:
request.url = "module3.swf";
request.applicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);
使用 IME 类
通过使用 IME 类,您可以在 Flash Player 中运用操作系统的 IME。
使用 ActionScript 可以确定以下内容:
用户的计算机上是否安装了 IME (Capabilities.hasIME)
用户计算机上是否启用了 IME (IME.enabled)
当前 IME 使用的转换模式 (IME.conversionMode)
可以使用特定 IME 上下文关联输入文本字段。在输入字段之间切换时,还可以在平假名(日文)、全角数字、半角数字、直接输入等之间切换 IME。
利用 IME,用户可键入多字节语言(例如中文、日文和韩文)的非 ASCII 文本字符。
如果用户计算机上 IME 未处于活动状态,则调用 IME 方法或属性(除 Capabilities.hasIME 之外)将失败。一旦手动激活 IME,随后对 IME 方法和属性的 ActionScript 调用即会正常运行。
查看是否已安装并启用了 IME
调用任何 IME 方法或属性之前,应始终检查用户计算机上当前是否已安装并启用 IME。先使用 Capabilities.hasIME 属性检查用户是否安装了 IME。如果将该属性设置为 true,则代码使用 IME.enabled 属性检查用户当前是否已启用了 IME。
确定当前启用的是哪种 IME 转换模式
通过将 IME.conversionMode 属性与 IMEConversionMode 类中的每个常量进行比较,检查当前 IME 使用的是哪种转换模式。
设置 IME 转换模式
更改用户的 IME 的转换模式时,您需要确保将代码封装在 try..catch 块中,因为使用 conversionMode 属性设置转换模式时,如果 IME 不能设置转换模式,则可能会引发错误。
侦听 IME 合成事件
设置合成字符串时会调度 IME 合成事件。例如,如果用户启用了 IME 并键入日文字符串,在用户选择合成字符串时,即会调度 IMEEvent.IME_COMPOSITION 事件。为了侦听 IMEEvent.IME_COMPOSITION 事件,您需要在 System 类的静态 ime 属性中添加一个事件侦听器 (flash.system.System.ime.addEventListener(...))。
Flash Player 安全性
Flash Player 安全性概述
Flash Player 安全性大部分基于加载的 SWF 文件、媒体和其它资源的原始域。来自特定 Internet 域(例如 www.loach.net.cn的 SWF 文件始终可以访问该域的所有数据。这些资源放置在相同的安全分组中,该分组称为"安全沙箱"。
SWF 文件可以加载 SWF 文件、位图、音频、文本文件以及自身域中的任何其它资源。此外,只要同一域中的两个 SWF 文件都是使用 ActionScript3.0 编写的,则始终可以在这两个文件之间执行跨脚本访问操作。"跨脚本访问"是指一个 SWF 文件能够使用 ActionScript 访问另一个 SWF 文件中的属性、方法和对象。对于使用 ActionScript 3.0 编写的 SWF 文件与使用 ActionScript 早期版本编写的 SWF 文件,它们之间不支持跨脚本访问;但是,可以通过使用 LocalConnection 类在这些文件之间进行通信。
默认情况下,以下基本安全性规则始终适用:
位于相同安全沙箱中的资源始终可以互相访问。
远程沙箱中的 SWF 文件始终不能访问本地文件和数据。
Flash Player 将以下地址视为单个域,并为每个地址设置单独的安全沙箱:
http://example.com
http://www.loach.net.cn
http://store.example.com
https://www.loach.net.cn
http://192.0.34.166
即使某个指定域(例如 http://example.com)映射到特定 IP 地址(例如 http://192.0.34.166),Flash Player 也会为它们设置单独的安全沙箱。
开发人员可以使用两种基本方法为 SWF 文件授予访问权限,使之能够访问除该 SWF 文件所属沙箱之外的其它沙箱中的资源:
Security.allowDomain() 方法
跨域策略文件
默认情况下,SWF 文件不能对其它域中的 ActionScript 3.0 SWF 文件执行跨脚本访问操作,也不能加载其它域中的数据。可以通过在加载的 SWF 文件中调用 Security.allowDomain() 方法来授予这种权限。
在 Flash Player 安全模型中,加载内容 与访问或加载数据 之间存在区别:
加载内容 ─"内容"被定义为媒体,包括 Flash Player 可以播放的可视化媒体、音频、视频或包含显示媒体的 SWF 文件。"数据"被定义为只有 ActionScript 代码才能访问的内容。可以使用 Loader、Sound 和 NetStream 等这样一些类来加载内容。
访问数据内容或加载数据 ─ 可以通过两种方式来访问数据:一种是从加载的媒体内容中提取数据,一种是从外部文件(例如 XML 文件)中直接加载数据。可以通过使用 Bitmap 对象、BitmapData.draw() 方法、Sound.id3 属性或者 SoundMixer.computeSpectrum() 方法从加载的媒体中提取数据,使用诸如 URLStream、URLLoader、Socket 和 XMLSocket 等类来加载数据。
Flash Player 安全模型针对加载内容和访问数据定义了不同的规则。通常,加载内容的限制要比访问数据的限制少一些。
通常,可以从任意位置加载内容(SWF 文件、位图、MP3 文件和视频),但是如果内容来自执行加载的 SWF 文件所在的域之外的域,则会将内容划分到单独的安全沙箱中。
下面是加载内容的一些限制:
默认情况下,本地 SWF 文件(从非网络地址加载的文件,例如用户硬盘上的文件)会被分类到只能与本地文件系统内容交互的沙箱中。这些文件无法从网络加载内容。
实时消息传递协议 (RTMP) 服务器可以限制对内容的访问。
如果加载的媒体为图像、音频或视频,则其安全沙箱之外的 SWF 文件无法访问该媒体的数据(如像素数据和声音数据),除非该 SWF 文件的域已包含在该媒体原始域的跨域策略文件中。
加载数据的其它格式包括文本文件或 XML 文件,这些文件可使用 URLLoader 对象来加载。同样,这种情况下要访问其它安全沙箱中的任何数据,必须通过原始域中的跨域策略文件来授予权限。
权限控制概述
Flash Player 客户端运行时安全模型是围绕 SWF 文件、本地数据和 Internet URL 等这些对象资源设计而成的模型。"资源持有者"是指拥有或使用这些资源的各方。资源持有者可以对其自己的资源进行控制(安全设置),每种资源有四个持有者。Flash Player 对这些控制严格采用一种权利层次结构,如下图所示:

安全控制层次结构
该图说明,如果管理员限制对资源的访问,则任何其他持有者都不能覆盖该限制。
管理用户控制
计算机的管理用户(使用管理权限登录的用户)可以应用能影响计算机所有用户的 Flash Player 安全设置。在非企业环境(例如家庭计算机)中,通常只有一个用户,该用户也拥有管理访问权限。即使是在企业环境中,单个用户也可以拥有计算机管理权限。
管理用户控制有两种类型:
mms.cfg 文件
"全局 Flash Player 信任"目录
mms.cfg 文件
在 Mac OS X 系统上,mms.cfg 文件位于 /Library/Application Support/Macromedia 中。在 Microsoft Windows 系统上,该文件位于系统目录的 Macromedia Flash Player 文件夹中(例如,在 Windows XP 默认安装中为 C:windowssystem32macromedflashmms.cfg)。
Flash Player 启动时将从此文件中读取其安全设置,然后使用这些设置限制功能。
mms.cfg 文件包括管理员用于执行以下任务的设置:
数据加载 ─ 限制读取本地 SWF 文件、禁止文件下载和上载以及对永久共享对象设置存储限制。
隐私控制 ─ 禁止麦克风和摄像头访问、禁止 SWF 文件播放无窗口内容,以及禁止与浏览器窗口中显示的 URL 不匹配的域中的 SWF 文件访问永久共享对象。
Flash Player 更新 ─ 设置检查 Flash Player 更新版本的时间间隔、指定检查 Flash Player 更新信息所使用的 URL、指定从其中下载 Flash Player 更新版本的 URL 以及完全禁用 Flash Player 的自动更新。
旧版文件支持 ─ 指定是否应将早期版本的 SWF 文件放置在受信任的本地沙箱中。
本地文件安全性 ─ 指定是否可以将本地文件放置在受信任的本地沙箱中。
全屏模式 ─ 禁用全屏模式。
SWF 文件可通过调用 Capabilities.avHardwareDisable 和 Capabilities.localFileReadDisable 属性来访问已禁用功能的某些信息。但是,mms.cfg 文件中的大部分设置无法通过 ActionScript 进行查询。
为对计算机强制执行与应用程序无关的安全和隐私策略,只能由系统管理员修改 mms.cfg 文件。mms.cfg 文件不能用于安装应用程序。虽然使用管理权限运行的安装程序可以修改 mms.cfg 文件的内容,但是 Adobe 将此类使用视为违反用户的信任,并且劝告安装程序的创建者决不要修改 mms.cfg 文件。
"全局 Flash Player 信任"目录
管理用户和安装应用程序可以将指定的本地 SWF 文件注册为受信任。这些 SWF 文件会被分配到受信任的本地沙箱。它们可以与任何其它 SWF 文件进行交互,也可以从任意位置(远程或本地)加载数据。文件在"全局 Flash Player 信任"目录中被指定为受信任,该目录与 mms.cfg 文件的所在目录相同,位置(特定于当前用户)如下:
Windows:systemMacromedFlashFlashPlayerTrust
(例如,C:windowssystem32MacromedFlashFlashPlayerTrust)
"Flash Player 信任"目录可以包含任意数目的文本文件,每个文件均列出受信任的路径,一个路径占一行。每个路径可以是单个的 SWF 文件、HTML 文件,也可以是目录。注释行以 # 号开头。例如,包含以下文本的 Flash Player 信任配置文件表示将向指定目录及所有子目录中的所有文件授予受信任状态:
# Trust files in the following directories:
C:Documents and SettingsAll UsersDocumentsSampleApp
信任配置文件中列出的路径应始终是本地路径或 SMB 网络路径。信任配置文件中的任何 HTTP 路径均会被忽略;只能信任本地文件。
为避免发生冲突,应为每个信任配置文件指定一个与安装应用程序相应的文件名,并且使用 .cfg 文件扩展名。
由于开发人员通过安装应用程序分发本地运行的 SWF 文件,因此可以让安装应用程序向"全局 Flash Player 信任"目录添加一个配置文件,为要分发的文件授予完全访问权限。安装应用程序必须由拥有管理权限的用户来运行。与 mms.cfg 文件不同,包含"全局 Flash Player 信任"目录是为了让安装应用程序授予信任权限。管理用户和安装应用程序都可以使用"全局 Flash Player 信任"目录指定受信任的本地应用程序。
此外,还有适用于单个用户的"Flash Player 信任"目录。
用户控制
Flash Player 提供三种不同的用户级别权限设置机制:"设置 UI"、"设置管理器"和"用户 Flash Player 信任"目录。
设置 UI 和设置管理器
"设置 UI"是一种用于配置特定域设置的快速交互机制。"设置管理器"显示一个更详细的界面,并提供全局更改功能,全局更改可影响对许多域或所有域拥有的权限。另外,当 SWF 文件请求新的权限,要求有关安全或隐私的运行时决策时,程序会显示一些对话框,用户可以在这些对话框中调整某些 Flash Player 设置。
"设置管理器"和"设置 UI"提供以下安全相关选项:
摄像头和麦克风设置 ─ 用户可以控制 Flash Player 对计算机上的摄像头和麦克风的访问。用户可以允许或拒绝对所有站点或特定站点的访问。如果用户没有为所有站点或特定站点指定设置,则当 SWF 文件试图访问摄像头或麦克风时,程序会显示一个对话框,让用户选择是否允许 SWF 文件访问该设备。用户也可以指定要使用的摄像头或麦克风,还可以设置麦克风的敏感度。
共享对象存储设置 ─ 用户可以选择域能够用来存储永久共享对象的磁盘空间量。用户可以对任意数量的特定域进行这些设置,还可以为新域指定默认设置。默认限制是 100 KB 磁盘空间。有关永久共享对象的详细信息,请参阅《ActionScript 3.0 语言和组件参考》中的 SharedObject 类。
在 mms.cfg 文件中所做的任何设置均不会反映在"设置管理器"中。
"用户 Flash Player 信任"目录
用户和安装应用程序可以将指定的本地 SWF 文件注册为受信任。这些 SWF 文件会被分配到受信任的本地沙箱。它们可以与任何其它 SWF 文件进行交互,也可以从任意位置(远程或本地)加载数据。用户在"用户 Flash Player 信任"目录中将文件指定为受信任,该目录与 Flash 共享对象存储区域的所在目录相同,位置(特定于当前用户)如下:
Windows:app dataMacromediaFlash Player#SecurityFlashPlayerTrust
(例如,C:Documents and SettingsJohnDApplication DataMacromediaFlash Player#SecurityFlashPlayerTrust)
这些设置只会影响当前用户,不会影响登录到计算机的其他用户。如果没有管理权限的用户在属于他们自己的系统中安装了某个应用程序,则"用户 Flash Player 信任"目录允许安装程序将该应用程序注册为该用户的受信任程序。
由于开发人员通过安装应用程序分发本地运行的 SWF 文件,因此可以让安装应用程序向"用户 Flash Player 信任"目录添加一个配置文件,为要分发的文件授予完全访问权限。即使在这种情况下,也将"用户 Flash Player 信任"目录文件视为用户控制,原因是用户操作(安装)启动了它。
此外,还有一个"全局 Flash Player 信任"目录,管理用户或安装程序可使用该目录为所有计算机用户注册应用程序。
Web 站点控制(跨域策略文件)
要使来自某个 Web 服务器的数据可用于来自其它域的 SWF 文件,可以在服务器上创建一个跨域策略文件。"跨域策略文件"是一个 XML 文件,它为服务器提供了一种方式,以指示该服务器的数据和文档可用于从某些域或所有域提供的 SWF 文件。服务器策略文件指定的域所提供的所有 SWF 文件都将被允许访问该服务器中的数据或资源。
跨域策略文件可影响对许多资源的访问,其中包括以下内容:
位图、声音和视频中的数据
加载 XML 和文本文件
对套接字和 XML 套接字连接的访问
将 SWF 文件从其它安全域导入到执行加载的 SWF 文件所在的安全域
策略文件语法
下面的示例显示了一个策略文件,该文件允许访问源自 *.example.com、www.friendOfExample.com 和 192.0.34.166 的 SWF 文件。
<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*.example.com" />
<allow-access-from domain="www.friendOfExample.com" />
<allow-access-from domain="192.0.34.166" />
</cross-domain-policy>
当某个 SWF 文件试图访问另一个域中的数据时,Flash Player 会尝试自动从该域加载策略文件。如果试图访问数据的 SWF 文件所在的域包括在该策略文件中,则数据将自动成为可访问数据。
默认情况下,策略文件必须命名为 crossdomain.xml,并且必须位于服务器的根目录中。但是,SWF 文件可以通过调用 Security.loadPolicyFile() 方法检查是否为其它名称或位于其它目录中。跨域策略文件仅适用于从其中加载该文件的目录及其子目录。因此,根目录中的策略文件适用于整个服务器,但是从任意子目录加载的策略文件仅适用于该目录及其子目录。
策略文件仅影响对其所在特定服务器的访问。例如,位于 https://www.loach.net.cn8080/crossdomain.xml 的策略文件只适用于在端口 8080 通过 HTTPS 对 www.loach.net.cn进行的数据加载调用。
跨域策略文件包含单个 <cross-domain-policy> 标签,该标签又包含零个或多个 <allow-access-from> 标签。每个 <allow-access-from> 标签包含一个属性 domain,该属性指定一个确切的 IP 地址、一个确切的域或一个通配符域(任何域)。通配符域由单个星号 (*)(匹配所有域和所有 IP 地址)或后接后缀的星号(只匹配那些以指定后缀结尾的域)表示。后缀必须以点开头。但是,带有后缀的通配符域可以匹配那些只包含后缀但不包含前导点的域。例如,foo.com 会被看作是 *.foo.com 的一部分。IP 域规范中不允许使用通配符。
如果您指定了一个 IP 地址,则只向使用 IP 语法从该 IP 地址加载的 SWF 文件(例如 http://65.57.83.12/flashmovie.swf)授予访问权限,而不向使用域名语法加载的 SWF 文件授予访问权限。Flash Player 不执行 DNS 解析。
您可以允许访问来自任何域的文档,如下面的示例所示:
<?xml version="1.0"?>
<!-- http://www.loach.net.cnrossdomain.xml -->
<cross-domain-policy>
<allow-access-from domain="*" />
</cross-domain-policy>
每个 <allow-access-from> 标签还具有可选的 secure 属性,其默认值为 true。如果您的策略文件在 HTTPS 服务器上,并且要允许非 HTTPS 服务器上的 SWF 文件从 HTTPS 服务器加载数据,则可以将此属性设置为 false。
将 secure 属性设置为 false 可能会危及 HTTPS 提供的安全性。特别是将此属性设置为 false 时,会使安全内容受到电子欺骗和窃听攻击。Adobe 强烈建议不要将 secure 属性设置为 false。
如果要加载的数据位于 HTTPS 服务器上,但是加载数据的 SWF 文件位于 HTTP 服务器上,则 Adobe 建议将要执行加载的 SWF 文件移动到 HTTPS 服务器上,以便可以使安全数据的所有副本都能得到 HTTPS 的保护。但是,如果决定必须将要执行加载的 SWF 文件保存在 HTTP 服务器上,则需将 secure="false" 属性添加到 <allow-access-from> 标签,如以下代码所示:
<allow-access-from domain="www.loach.net.cn secure="false" />
不包含任何 <allow-access-from> 标签的策略文件相当于服务器上没有策略。
套接字策略文件
ActionScript 对象可实例化两种不同的服务器连接:基于文档的服务器连接和套接字连接。Loader、Sound、URLLoader 和 URLStream 等 ActionScript 对象可实例化基于文档的服务器连接,这些对象均根据 URL 加载文件。ActionScript Socket 和 XMLSocket 对象进行套接字连接,这些对象操作的是数据流而非加载的文档。Flash Player 支持两种策略文件:基于文档的策略文件和套接字策略文件。基于文档的连接需要基于文档的策略文件,套接字连接则需要套接字策略文件。
Flash Player 要求使用尝试连接希望使用的同类协议传输策略文件。例如,如果将策略文件放置在您的 HTTP 服务器上,则允许其它域中的 SWF 文件从该服务器(作为 HTTP 服务器)加载数据。但是,如果在这台服务器上未提供套接字策略文件,则禁止其它域的 SWF 文件在套接字级别连接到该服务器。检索套接字策略文件的方法必须与连接方法相匹配。
由套接字服务器提供的策略文件具有与任何其它策略文件相同的语法,只是前者还必须指定要对哪些端口授予访问权限。如果策略文件来自低于 1024 的端口号,则它可以对任何端口授予访问权限;如果策略文件来自 1024 或更高的端口,则它只能对 1024 端口和更高的端口授予访问权限。允许的端口在 <allow-access-from> 标签的 to-ports 属性中指定。单个端口号、端口范围和通配符都是允许值。
下面是一个 XMLSocket 策略文件示例:
<cross-domain-policy>
<allow-access-from domain="*" to-ports="507" />
<allow-access-from domain="*.example.com" to-ports="507,516" />
<allow-access-from domain="*.example2.com" to-ports="516-523" />
<allow-access-from domain="www.loach.net.cn to-ports="507,516-523" />
<allow-access-from domain="www.loach.net.cn to-ports="*" />
</cross-domain-policy>
在 Flash Player 6 中首次引入策略文件时,并不支持套接字策略文件。与套接字服务器的连接由跨域策略文件所在默认位置中的一个策略文件授权,跨域策略文件位于与套接字服务器位于同一个域中的 HTTP 服务器的端口 80 上。为尽可能保留现有的服务器排列,Flash Player 9 仍然支持此功能。但是,Flash Player 现在的默认设置是在与套接字连接相同的端口上检索套接字策略文件。如果希望使用基于 HTTP 的策略文件来授权套接字连接,则必须使用如下所示代码显式请求 HTTP 策略文件:
Security.loadPolicyFile("http://socketServerHost.com/crossdomain.xml")
此外,为授权套接字连接,HTTP 策略文件只能来自跨域策略文件的默认位置,而非来自任何其它 HTTP 位置。从 HTTP 服务器获取的策略文件隐式向 1024 和所有更高端口授予套接字访问权限;HTTP 策略文件中的任何 to-ports 属性均被忽略。
预加载策略文件
从服务器加载数据或连接到套接字是一种异步操作,Flash Player 只是等待跨域策略文件完成下载,然后才开始主操作。但是,从图像中提取像素数据或从声音中提取采样数据是一种同步操作,跨域策略文件必须在可以提取数据之前先加载数据。加载媒体时,需要指定媒体检查是否存在跨域策略文件:
使用 Loader.load() 方法时,设置 context 参数的 checkPolicyFile 属性,该参数是一个 LoaderContext 对象。
使用 <img> 标签在文本字段中嵌入图像时,将 <img> 标签的 checkPolicyFile 属性设置为 "true",如下所示:<img checkPolicyFile = "true" src = "example.jpg">。
使用 Sound.load() 方法时,设置 context 参数的 checkPolicyFile 属性,该参数是一个 SoundLoaderContext 对象。
使用 NetStream 类时,设置 NetStream 对象的 checkPolicyFile 属性。
设置上述参数时,Flash Player 首先会检查是否已经为该域下载了任何策略文件。然后考虑对 Security.loadPolicyFile() 方法的任何待定调用,以便查看它们是否在范围内,如果在范围内,则等待调用完成。然后,它查找服务器上默认位置中的跨域策略文件。
作者(开发人员)控制
用于授予安全权限的主 ActionScript API 是 Security.allowDomain() 方法,它将向指定域中的 SWF 文件授予权限。在下面的示例中,SWF 文件向 www.loach.net.cn域提供的 SWF 文件授予访问权限:
Security.allowDomain("www.loach.net.cn)
此方法为下列各项授予权限:
SWF 文件之间的跨脚本访问
显示列表访问
事件检测
对 Stage 对象的属性和方法的完全访问
调用 Security.allowDomain() 方法的主要目的是为外部域中的 SWF 文件授予权限以访问调用 Security.allowDomain() 方法的 SWF 文件的脚本。
如果将 IP 地址指定为 Security.allowDomain() 方法的参数,则不允许任何源自该指定 IP 地址的访问方进行访问。相反,只允许 URL 中包含该指定 IP 地址的访问方进行访问,而不允许其域名映射到该 IP 地址的访问方进行访问。例如,如果域名 www.loach.net.cn映射到 IP 地址 192.0.34.166,则对 Security.allowDomain("192.0.34.166") 的调用不会授予对 www.loach.net.cn的访问权限。
可以将通配符"*"传递给 Security.allowDomain() 方法以允许从所有域进行访问。由于这种方式会为"所有"域中的 SWF 文件授予访问执行调用的 SWF 文件的脚本的权限,因此请谨慎使用通配符"*"。
ActionScript 还包括一个权限 API,称为 Security.allowInsecureDomain()。此方法与 Security.allowDomain() 方法的作用相同,只是从安全 HTTPS 连接提供的 SWF 文件调用时,此方法还会允许非安全协议(例如 HTTP)提供的其它 SWF 文件访问执行调用的 SWF 文件。但是,在安全协议 (HTTPS) 中的文件与非安全协议(例如 HTTP)中的文件之间执行脚本访问操作并不是一种好的安全性做法;这样做会使安全内容受到电子欺骗和窃听攻击。下面是此类攻击的作用方式:由于 Security.allowInsecureDomain() 方法允许通过 HTTP 连接提供的 SWF 文件访问安全 HTTPS 数据,因此介入 HTTP 服务器和用户之间的攻击者能够将 HTTP SWF 文件替换为它们自己的文件,这样便可访问您的 HTTPS 数据。
另一种与安全性相关的重要方法是 Security.loadPolicyFile() 方法,该方法可让 Flash Player 在非标准位置检查是否存在跨域策略文件。
安全沙箱
客户端计算机可以从很多来源(如外部 Web 站点或本地文件系统)中获取单个 SWF 文件。当 SWF 文件及其它资源(例如共享对象、位图、声音、视频和数据文件)加载到 Flash Player 中时,Flash Player 会根据这些文件和资源的来源单独地将其分配到安全沙箱中。
远程沙箱
Flash Player 将来自 Internet 的资源(包括 SWF 文件)分类到单独的沙箱中,这些沙箱与各自 Web 站点原始域相对应。默认情况下,对这些文件授予访问其自身所在服务器中任何资源的权限。通过显式的 Web 站点许可和作者许可(例如跨域策略文件和 Security.allowDomain() 方法),可以允许远程 SWF 文件访问其它域的其它数据。
远程 SWF 文件无法加载任何本地文件或资源。
本地沙箱
"本地文件"是指通过使用 file: 协议或统一命名约定 (UNC) 路径引用的任何文件。本地 SWF 文件放置在三个本地沙箱中的一个内:
只能与本地文件系统内容交互的沙箱 ─ 出于安全性考虑,Flash Player 在默认情况下将所有本地 SWF 文件和资源放置在只能与本地文件系统内容交互的沙箱中。通过此沙箱,SWF 文件可以读取本地文件(例如通过使用 URLLoader 类),但是它们无法以任何方式与网络进行通信。这样可向用户保证本地数据不会泄漏到网络或以其它方式不适当地共享。
只能与远程内容交互的沙箱 ─ 编译 SWF 文件时,可以指定该文件作为本地文件运行时拥有网络访问权限。这些文件放置在只能与远程内容交互的沙箱中。分配到只能与远程内容交互的沙箱中的 SWF 文件将失去其本地文件访问权限,但允许这些 SWF 文件访问网络中的数据。不过,只有通过跨域策略文件或调用 Security.allowDomain() 方法来授予操作权限,才允许远程内容交互的 SWF 文件读取源自网络的数据。为授予此类权限,跨域策略文件必须向"所有"域授予权限,方法是使用 <allow-access-from domain="*"/> 或使用 Security.allowDomain("*")。
受信任的本地沙箱 ─ 注册为受信任(由用户或安装程序注册)的本地 SWF 文件放置在受信任的本地沙箱中。系统管理员和用户还可以根据安全注意事项将本地 SWF 文件重新分配(移动)到受信任的本地沙箱,或者从该沙箱中进行重新分配。分配到受信任的本地沙箱的 SWF 文件可以与其它任何 SWF 文件交互,也可以从任何位置(远程或本地)加载数据。
只能与远程内容交互的沙箱和只能与本地文件系统内容交互的沙箱之间的通信以及只能与本地文件系统内容交互的沙箱和远程沙箱之间的通信是严格禁止的。Flash 应用程序或用户/管理员不能授予允许此类通信的权限。
在本地 HTML 文件和本地 SWF 文件之间以任一方向访问脚本(例如使用 ExternalInterface 类)均要求涉及的 HTML 文件和 SWF 文件应位于受信任的本地沙箱中。这是因为浏览器的本地安全模型与 Flash Player 本地安全模型不同。
只能与远程内容交互的沙箱中的 SWF 文件无法加载只能与本地文件系统内容交互的沙箱中的 SWF 文件。只能与本地文件系统内容交互的沙箱中的 SWF 文件无法加载只能与远程内容交互的沙箱中的 SWF 文件。
设置本地 SWF 文件的沙箱类型
通过在 Adobe Flash CS3 创作工具中设置文档发布设置,您可以配置只能与本地文件系统内容交互的沙箱或只能与远程内容交互的沙箱的 SWF 文件。
最终用户或计算机管理员可以指定某个本地 SWF 文件是受信任的,以允许该文件从所有域(本地和网络)加载数据。这一点在"全局 Flash Player 信任"目录和"用户 Flash Player 信任"目录中指定。
Security.sandboxType 属性
SWF 文件的作者可以使用只读的静态 Security.sandboxType 属性来确定 Flash Player 向其分配该 SWF 文件的沙箱类型。Security 类包括表示 Security.sandboxType 属性可能值的常量,如下所示:
Security.REMOTE ─ SWF 文件来自 Internet URL,并遵守基于域的沙箱规则。
Security.LOCAL_WITH_FILE ─ SWF 文件是本地文件,但尚未受到用户信任,且没有使用网络名称进行发布。此 SWF 文件可以从本地数据源读取数据,但无法与 Internet 进行通信。
Security.LOCAL_WITH_NETWORK ─ SWF 文件是本地文件,且尚未受到用户信任,但已使用网络名称进行发布。此 SWF 文件可与 Internet 通信,但不能从本地数据源读取数据。
Security.LOCAL_TRUSTED ─ SWF 文件是本地文件,且已使用"设置管理器"或 Flash Player 信任配置文件受到用户信任。此 SWF 文件既可以从本地数据源读取数据,也可以与 Internet 进行通信。
限制网络 API
可以控制 SWF 文件对网络功能的访问,方法是通过在包含 SWF 内容的 HTML 页面的 <object> 和 <embed> 标签中设置 allowNetworking 参数。
allowNetworking 的可能值包括:
"all"(默认值)─ SWF 文件中允许使用所有网络 API。
"internal"─ SWF 文件可能不调用浏览器导航或浏览器交互 API(在本节后面部分中列出),但是它会调用任何其它网络 API。
"none"─ SWF 文件可能不调用浏览器导航或浏览器交互 API(在本节后面部分中列出),并且它无法使用任何 SWF 到 SWF 通信 API(也在本节后面部分中列出)。
调用被禁止的 API 会引发 SecurityError 异常。
要设置 allowNetworking 参数,在包含 SWF 文件引用的 HTML 页面的 <object> 和 <embed> 标签中添加 allowNetworking 参数并设置参数值。
HTML 页面也可能会使用脚本来生成 SWF 嵌入式标签。您需要更改该脚本,以便让它能够插入适当的 allowNetworking 设置。由 Flash 和 Adobe Flex Builder 生成的 HTML 页面使用 AC_FL_RunContent() 函数嵌入 SWF 文件的引用,您需要将 allowNetworking 参数设置添加到该脚本,如下所示:
AC_FL_RunContent( ... "allowNetworking", "none", ...)
当 allowNetworking 设置为 "internal" 时,以下 API 被禁止:
navigateToURL() 、fscommand() 、ExternalInterface.call() 。
当 allowNetworking 设置为 "none" 时,除了上面列出的那些 API 外,还会禁止以下 API:
sendToURL()、 FileReference.download()、 FileReference.upload()、 Loader.load() 、LocalConnection.connect()、 LocalConnection.send()、 NetConnection.connect()、 NetStream.play()、Security.loadPolicyFile()、 SharedObject.getLocal()、 SharedObject.getRemote()、Socket.connect() 、Sound.load()、 URLLoader.load()、 URLStream.load()、 XMLSocket.connect()。
即使所选 allowNetworking 设置允许 SWF 文件使用网络 API,但根据安全沙箱的限制,还可能存在其它限制。
当 allowNetworking 设置为"none"时,无法在 TextField 对象 htmlText 属性的 <img> 标签中引用外部媒体(这会引发 SecurityError 异常)。
当 allowNetworking 设置为 "none" 时,从导入的共享库添加到 Flash 创作工具(而不是 ActionScript)中的元件在运行时被阻止。
全屏模式安全性
Flash Player 9.0.27.0 和更高版本支持全屏模式,在该模式中 Flash 内容可以填满整个屏幕。要进入全屏模式,需将 Stage 的 displayState 属性设置为 StageDisplayState.FULL_SCREEN 常量。对于在浏览器中运行的 SWF 文件,存在一些安全注意事项。
要启用全屏模式,请在包含 SWF 文件引用的 HTML 页面的 <object> 和 <embed> 标签中添加 allowFullScreen 参数,并将参数值设置为"true"(默认值为"false")。
HTML 页面也可能会使用脚本来生成 SWF 嵌入式标签。您必须更改该脚本,以便让它能够插入适当的 allowFullScreen 设置。由 Flash 和 Flex Builder 生成的 HTML 页面使用 AC_FL_RunContent() 函数嵌入 SWF 文件的引用,您需要添加 allowFullScreen 参数设置,如下所示:
AC_FL_RunContent( ... "allowFullScreen", "true", ...)
仅当在响应鼠标事件或键盘事件时才会调用启动全屏模式的 ActionScript。如果在其它情况中调用,Flash Player 会引发异常。
在全屏模式下,用户无法在文本输入字段中输入文本。所有键盘输入和键盘相关的 ActionScript 在全屏模式下均会被禁用,但将应用程序返回标准模式的键盘快捷键(例如按 Esc)除外。
当内容进入全屏模式时,程序会显示一条消息,指导用户如何退出和返回标准模式。该消息将显示几秒钟,然后淡出。
如果某个调用方与 Stage 所有者(主 SWF 文件)没有位于同一安全沙箱,则调用 Stage 对象的 displayState 属性会引发异常。
管理员可以通过在 mms.cfg 文件中设置 FullScreenDisable = 1 对浏览器中运行的 SWF 文件禁用全屏模式。
在浏览器中,必须在 HTML 页面中包含 SWF 文件,才能进入全屏模式。
在独立的播放器或放映文件中始终允许全屏模式。
加载内容
SWF 文件可以加载以下内容类型: SWF文件、 图像、声音、 视频 。
加载 SWF 文件和图像
使用 Loader 类加载 SWF 文件和图像(JPG、GIF 或 PNG 文件)。除只能与本地文件系统内容交互的沙箱中的 SWF 文件之外,其它所有 SWF 文件都可以从任何网络域加载 SWF 文件和图像。只有本地沙箱中的 SWF 文件才能从本地文件系统中加载 SWF 文件和图像。但是,只能与远程内容交互的沙箱中的文件只能加载位于受信任的本地沙箱或只能与远程内容交互的沙箱中的本地 SWF 文件。只能与远程内容交互的沙箱中的 SWF 文件可加载非 SWF 文件(例如图像)的本地内容,但是无法访问所加载内容中的数据。
从不受信任的来源(如 Loader 对象的根 SWF 文件所在域以外的域)加载 SWF 文件时,您可能需要为 Loader 对象定义遮罩,以防止加载的内容(Loader 对象的子级)绘制到该遮罩之外的 Stage 部分中。
当调用 Loader 对象的 load() 方法时,可以指定一个 context 参数,该参数是一个 LoaderContext 对象。LoaderContext 类包括三个属性,用于定义如何使用加载的内容的上下文:
checkPolicyFile:仅当加载图像文件(不是 SWF 文件)时才会使用此属性。如果图像文件所在的域与包含 Loader 对象的文件所在的域不同,则指定此属性。如果将此属性设置为 true,Loader 将检查跨域策略文件的原始服务器。如果服务器授予 Loader 域适当权限,则来自 Loader 域中 SWF 文件的 ActionScript 可以访问所加载图像中的数据。换言之,可以使用 Loader.content 属性获取对表示所加载图像的 Bitmap 对象的引用,或使用 BitmapData.draw() 方法访问所加载图像中的像素。
securityDomain:仅当加载 SWF 文件(不是图像)时才会使用此属性。如果 SWF 文件所在的域与包含 Loader 对象的文件所在的域不同,则指定此属性。对于 securityDomain 属性而言,目前仅支持以下两个值:null(默认值)和 SecurityDomain.currentDomain。如果指定 SecurityDomain.currentDomain,则要求加载的 SWF 文件应"导入"到执行加载的 SWF 文件所在的沙箱中,这意味着其运行方式就像它已从执行加载的 SWF 文件自己的服务器中加载一样。只有在位于加载的 SWF 文件服务器上找到跨域策略文件时才允许这样做,从而允许执行加载的 SWF 文件所在的域进行访问。如果找到所需的策略文件,则一旦加载开始,加载方和被加载方可以自由地互相访问脚本,原因是它们位于同一沙箱中。请注意,多数情况可以通过执行普通加载操作然后让加载的 SWF 文件调用 Security.allowDomain() 方法来取代沙箱导入。由于加载的 SWF 文件将位于自己的原始沙箱中,并因而能够访问自己实际服务器上的资源,因此后一种方法会更易于使用。
applicationDomain:仅当加载使用 ActionScript 3.0 编写的 SWF 文件(不是图像或使用 ActionScript 1.0 或 2.0 编写的 SWF 文件)时才会使用此属性。当加载文件时,可以指定文件应放置在特定的应用程序域中,而不是默认放置在一个新的应用程序域中,这个新的应用程序域是执行加载的 SWF 文件所在应用程序域的子域。请注意,应用程序域是安全域的子单位,因此仅当要加载的 SWF 文件由于以下原因来自您自己的安全域时,才能指定目标应用程序域:该文件来自您自己的服务器,或者使用 securityDomain 属性已成功地将该文件导入到您的安全域中。如果指定应用程序域,但加载的 SWF 文件属于其它安全域,则在 applicationDomain 中指定的域将被忽略。
Loader 对象的一个重要属性就是 contentLoaderInfo 属性,该属性是一个 LoaderInfo 对象。与大部分对象不同,LoaderInfo 对象在执行加载的 SWF 文件和被加载的内容之间共享,并且双方始终可以访问该对象。当被加载的内容为 SWF 文件时,它可以通过 DisplayObject.loaderInfo 属性访问 LoaderInfo 对象。LoaderInfo 对象包括诸如加载进度、加载方和被加载方的 URL、加载方和被加载方之间的信任关系等信息及其它信息。
加载声音和视频
除只能与本地文件系统内容交互的沙箱中的那些 SWF 文件之外,所有 SWF 文件都允许从网络来源加载声音和视频,使用 Sound.load()、NetConnection.connect() 和 NetStream.play() 方法即可。
只有本地 SWF 文件才能从本地文件系统加载媒体。只有只能与本地文件系统内容交互的沙箱或受信任的本地沙箱中的 SWF 文件才能访问这些加载文件中的数据。
对加载的媒体还存在一些其它数据访问限制。
使用文本字段中的 <img> 标签加载 SWF 文件和图像
通过使用 <img> 标签,可以将 SWF 文件和位图加载到文本字段中,如以下代码所示:
<img src = 'filename.jpg' id = 'instanceName' >
通过使用 TextField 实例的 getImageReference() 方法,可以访问以这种方式加载的内容,如以下代码所示:
var loadedObject:DisplayObject = myTextField.getImageReference('instanceName');
但是请注意,以这种方式加载的 SWF 文件和图像会被放入与各自来源相应的沙箱中。
当在文本字段中使用 <img> 标签加载图像文件时,通过跨域策略文件可以允许访问图像中的数据。通过将 checkPolicyFile 属性添加到 <img> 标签上,可以检查是否存在策略文件,如以下代码所示:
<img src = 'filename.jpg' checkPolicyFile = 'true' id = 'instanceName' >
当在文本字段中使用 <img> 标签加载 SWF 时,可以允许通过调用 Security.allowDomain() 方法来访问该 SWF 文件的数据。
当在文本字段中使用 <img> 标签加载外部文件时(相对于使用嵌在 SWF 文件中的 Bitmap 类),会自动创建一个 Loader 对象作为 TextField 对象的子对象,并且会将外部文件加载到该 Loader 对象中,就如同使用了 ActionScript 中的 Loader 对象来加载文件一样。在这种情况下,getImageReference() 方法返回自动创建的 Loader。由于此 Loader 对象与调用代码位于同一安全沙箱中,因此访问此对象不需要任何安全检查。
但是,当引用 Loader 对象的 content 属性来访问加载的媒体时,需要应用安全性规则。如果内容是图像,则需要实现跨域策略文件;如果内容是 SWF 文件,则需要让 SWF 文件中的代码调用 allowDomain() 方法。
使用 RTMP 服务器传送的内容
Flash Media Server 使用实时媒体协议 (RTMP) 提供数据、音频和视频。SWF 文件通过使用 NetConnection 类的 connect() 方法并作为参数传递 RTMP URL 来加载此媒体。Flash Media Server 可以根据所请求文件的域来限制连接并防止内容被下载。
对于从 RTMP 源加载的媒体,不能使用 BitmapData.draw() 和 SoundMixer.computeSpectrum() 方法来提取运行时图形和声音数据。
跨脚本访问
如果两个使用 ActionScript 3.0 编写的 SWF 文件来自同一个域,例如,一个 SWF 文件的 URL 是 http://www.loach.net.cnswfA.swf,另一个文件的 URL 是 http://www.loach.net.cnswfB.swf,则一个 SWF 文件可以检查并修改另一个 SWF 文件中的变量、对象、属性、方法等等,反之亦然。这称为“跨脚本访问”。
在 AVM1 SWF 文件和 AVM2 SWF 文件之间不支持跨脚本访问。AVM1 SWF 文件是使用 ActionScript 1.0 或 ActionScript 2.0 创建的文件。(AVM1 和 AVM2 指的是 ActionScript 虚拟机。)但是,可以使用 LocalConnection 类在 AVM1 和 AVM2 之间发送数据。
如果两个使用 ActionScript 3.0 编写的 SWF 文件来自不同的域(例如,http://siteA.com/swfA.swf 和 http://siteB.com/swfB.swf),则在默认情况下,Flash Player 既不允许 swfA.swf 访问 swfB.swf 的脚本,也不允许 swfB.swf 访问 swfA.swf 的脚本。通过调用 Security.allowDomain(),一个 SWF 文件可向其它域中的 SWF 文件授予访问其脚本的权限。通过调用 Security.allowDomain("siteA.com"),swfB.swf 向来自 siteA.com 的 SWF 文件授予访问其脚本的权限。
在任何跨域的情况下,明确所涉及的双方非常重要。为了便于进行此讨论,我们将执行跨脚本访问的一方称为“访问方”(通常是执行访问的 SWF),将另一方称为“被访问方”(通常是被访问的 SWF)。当 siteA.swf 访问 siteB.swf 的脚本时,siteA.swf 是访问方,siteB.swf 是被访问方,如下图所示:


使用 Security.allowDomain() 方法建立的跨域权限是不对称的。在上例中,siteA.swf 可以访问 siteB.swf 的脚本,但 siteB.swf 无法访问 siteA.swf 的脚本,这是因为 siteA.swf 未调用 Security.allowDomain() 方法来授予 siteB.com 中的 SWF 文件访问其脚本的权限。可以通过使两个 SWF 文件都调用 Security.allowDomain() 方法来设置对称权限。
除了防止 SWF 文件受到其它 SWF 文件发起的跨域脚本访问外,Flash Player 还防止 SWF 文件受到 HTML 文件发起的跨域脚本访问。可以通过 ExternalInterface.addCallback() 方法建立的回调执行 HTML 到 SWF 的脚本访问。当 HTML 到 SWF 的脚本访问跨域时,被访问的 SWF 文件必须调用 Security.allowDomain() 方法(这与访问方是 SWF 文件时一样),否则操作将失败。
此外,Flash Player 还对 SWF 到 HTML 的脚本访问提供安全控制。
Stage 安全性
Stage 对象的某些属性和方法可用于显示列表中的任何 sprite 或影片剪辑。
但是,我们说 Stage 对象有一个所有者,即加载的第一个 SWF 文件。默认情况下,Stage 对象的以下属性和方法只能用于与舞台所有者位于同一安全沙箱中的 SWF 文件:
属性
方法
align
showDefaultContextMenu
addChild()
displayState
stageFocusRect
addChildAt()
frameRate
stageHeight
addEventListener()
height
stageWidth
dispatchEvent()
mouseChildren
tabChildren
hasEventListener()
numChildren
textSnapshot
setChildIndex()
quality
width
willTrigger()
scaleMode


为使与 Stage 所有者不在同一沙箱中的 SWF 文件能够访问这些属性和方法,舞台所有者 SWF 文件必须调用 Security.allowDomain() 方法来允许外部沙箱的域。
frameRate 属性是一种特殊情况:任何 SWF 文件均能读取 frameRate 属性。但是,只有位于 Stage 所有者的安全沙箱中的文件(或通过调用 Security.allowDomain() 方法被授予权限的文件)才能更改该属性。
此外,对于 Stage 对象的 removeChildAt() 和 swapChildrenAt() 方法还存在一些限制,但是这些限制与其它限制不同。要调用这些方法,不需要与 Stage 所有者位于同一域中,而是代码必须与受影响的子对象位于同一域中,或者子对象可以调用 Security.allowDomain() 方法。
遍历显示列表
一个 SWF 文件能够访问从其它沙箱中加载的显示对象受到一定限制。为使 SWF 文件能够访问由其它沙箱中的另一个 SWF 文件创建的显示对象,被访问的 SWF 文件必须调用 Security.allowDomain() 方法向进行访问的 SWF 文件所在的域授予访问权限。
要访问由 Loader 对象加载的 Bitmap 对象,图像文件的原始服务器上必须存在跨域策略文件,并且该跨域策略文件必须为尝试访问该 Bitmap 对象的 SWF 文件所在的域授予访问权限。
与加载的文件(和 Loader 对象)相对应的 LoaderInfo 对象包括以下三种属性(定义加载的对象和 Loader 对象之间的关系):childAllowsParent、parentAllowsChild 和 sameDomain。
事件安全性
根据调度事件的显示对象所在的沙箱,与显示列表相关的事件具有一定的安全性访问限制。显示列表中的事件具有冒泡阶段和捕获阶段(在处理事件中论述)。在冒泡阶段和捕获阶段期间,事件从源显示对象开始迁移,并经过显示列表中的父显示对象。如果父对象与源显示对象位于不同的安全沙箱中,则捕获阶段和冒泡阶段在父对象的下方停止,除非父对象的所有者与源对象的所有者互相信任。这种互相信任可以通过以下方式来建立:
拥有父对象的 SWF 文件必须调用 Security.allowDomain() 方法,以信任拥有源对象的 SWF 文件所在的域。
拥有源对象的 SWF 文件必须调用 Security.allowDomain() 方法,以信任拥有父对象的 SWF 文件所在的域。
与加载的文件(和 Loader 对象)相对应的 LoaderInfo 对象包括以下两种属性(定义加载的对象和 Loader 对象之间的关系):childAllowsParent 和 parentAllowsChild。
对于从显示对象以外的对象调度的事件,不存在任何安全检查或与安全相关的含义。
作为数据访问加载的媒体
默认情况下,一个安全沙箱中的 SWF 文件无法从另一个沙箱中加载的媒体所呈现或播放的图形或音频对象中获取像素数据或音频数据。但是,可以使用以下方法授予这种权限:
在加载的 SWF 文件中,调用 Security.allowDomain() 方法可授予对其它域中 SWF 文件的数据访问权限。
对于加载的图像、声音或视频,在被加载文件所在的服务器上添加跨域策略文件。此策略文件必须向试图调用 BitmapData.draw() 或 SoundMixer.computeSpectrum() 方法的 SWF 文件所在的域授予访问权限,以便从文件提取数据。
访问位图数据
借助 BitmapData 对象的 draw() 方法,可以将任何显示对象的当前显示像素绘制到 BitmapData 对象。这样能够包括 MovieClip 对象、Bitmap 对象或任何显示对象的像素。要使用 draw() 方法将像素绘制到 BitmapData 对象,必须满足以下条件:
如果是除所加载位图之外的源对象,则该源对象及其(如果是 Sprite 或 MovieClip 对象)所有子对象必须与调用 draw() 方法的对象位于同一域,或者它们必须位于调用方通过调用 Security.allowDomain() 方法可访问的 SWF 文件中。
如果是已加载的位图源对象,则该源对象必须与调用 draw() 方法的对象位于同一域,或者其源服务器必须包含一个授予调用域访问权限的跨域策略文件。
如果不满足上述条件,则会引发 SecurityError 异常。
当使用 Loader 类的 load() 方法加载图像时,可以指定一个 context 参数,该参数是一个 LoaderContext 对象。如果将 LoaderContext 对象的 checkPolicyFile 属性设置为 true,则 Flash Player 将在从其中加载该图像的服务器上检查是否存在跨域策略文件。如果存在跨域策略文件且该文件允许执行加载的 SWF 文件所在的域进行访问,则会允许该文件访问 Bitmap 对象中的数据,否则就不允许。
此外,还可以通过文本字段中的 <img> 标签在加载的图像中指定 checkPolicyFile 属性。
访问声音数据
以下与声音相关的 ActionScript 3.0 API 存在一些安全限制:
SoundMixer.computeSpectrum() 方法 ─ 对于与声音文件位于同一安全沙箱的 SWF 文件,始终允许使用该方法。对于其它沙箱中的文件,则需经过安全检查。
SoundMixer.stopAll() 方法 ─ 对于与声音文件位于同一安全沙箱的 SWF 文件,始终允许使用该方法。对于其它沙箱中的文件,则需经过安全检查。
Sound 类的 id3 属性 ─ 对于与声音文件位于同一安全沙箱的 SWF 文件,始终允许使用该属性。对于其它沙箱中的文件,则需经过安全检查。
每个声音都具有两种与之关联的沙箱(一个内容沙箱和一个所有者沙箱):
声音的源域确定内容沙箱,内容沙箱则确定是否可以通过声音的 id3 属性和 SoundMixer.computeSpectrum() 方法提取声音中的数据。
启动声音播放的对象确定所有者沙箱,所有者沙箱则确定是否可以使用 SoundMixer.stopAll() 方法停止声音。
当使用 Sound 类的 load() 方法加载声音时,可以指定一个 context 参数,该参数是一个 SoundLoaderContext 对象。如果将 SoundLoaderContext 对象的 checkPolicyFile 属性设置为 true,则 Flash Player 将在从其中加载该声音的服务器上检查是否存在跨域策略文件。如果存在跨域策略文件且该文件允许执行加载的 SWF 文件所在的域进行访问,则会允许该文件访问 Sound 对象的 id 属性,否则就不允许。此外,设置 checkPolicyFile 属性可以为加载的声音启用 SoundMixer.computeSpectrum() 方法。
可以使用 SoundMixer.areSoundsInaccessible() 方法确定对 SoundMixer.stopAll() 方法的调用是否会因调用方无法访问一个或多个声音所有者的沙箱而停止全部声音。
调用 SoundMixer.stopAll() 方法会停止与 stopAll() 的调用方位于同一所有者沙箱中的那些声音。它还会停止由调用 Security.allowDomain() 方法的 SWF 文件来启动播放的声音,以允许调用 stopAll() 方法的 SWF 文件所在的域进行访问。任何其它声音均不会停止,可以通过调用 SoundMixer.areSoundsInaccessible() 方法来确定此类声音是否存在。
调用 computeSpectrum() 方法要求播放的每个声音应与调用该方法的对象位于同一沙箱中,或者位于已向调用方的沙箱授予权限的源;否则会引发 SecurityError 异常。对于从 SWF 文件库中嵌入声音加载的声音,通过在加载的 SWF 文件中调用 Security.allowDomain() 方法授予权限。对于从非 SWF 文件的源(源自加载的 MP3 文件或 Flash 视频)中加载的声音,源服务器上的跨域策略文件将授予访问所加载媒体中数据的权限。如果声音是从 RTMP 数据流加载的,则无法使用 computeSpectrum() 方法。
访问视频数据
可以使用 BitmapData.draw() 方法捕获视频当前帧中的像素数据。
视频有两种不同形式:
RTMP 视频
渐进式视频(该视频是从没有 RTMP 服务器的 FLV 文件加载的)
无法使用 BitmapData.draw() 方法访问 RTMP 视频。
当调用 BitmapData.draw() 方法,并且 source 参数为渐进式视频时,BitmapData.draw() 的调用方必须与 FLV 文件位于同一沙箱,或者 FLV 文件所在的服务器上必须存在一个策略文件,用以向执行调用的 SWF 文件所在的域授予访问权限。通过将 NetStream 对象的 checkPolicyFile 属性设置为 true,可以请求下载该策略文件。
加载数据
SWF 文件可以将服务器的数据加载到 ActionScript 中,也可以将 ActionScript 中的数据发送到服务器。加载数据与加载媒体的操作方式不同,原因是加载的信息将直接显示在 ActionScript 中,而不是作为媒体显示。通常,SWF 文件可以从它们自己的域中加载数据。但是,要从其它域加载数据,它们通常需要跨域策略文件。
使用 URLLoader 和 URLStream
可以加载诸如 XML 文件或文本文件等数据。URLLoader 和 URLStream 类的 load() 方法受到跨域策略文件权限的控制。
如果使用 load() 方法从与调用该方法的 SWF 文件所在域不同的域中加载内容,则 Flash Player 会在被加载资源所在的服务器上检查是否存在跨域策略文件。如果存在跨域策略文件,并且该文件向执行加载的 SWF 文件所在的域授予访问权限,则可以加载数据。
连接到套接字
默认情况下,禁用对套接字和 XML 套接字连接的跨域访问。此外,默认情况下还禁止访问与低于 1024 的端口上的 SWF 文件位于同一个域的套接字连接,但可以通过提供以下任一位置中的跨域策略文件来允许访问这些端口:
与主套接字连接相同的端口
不同端口
与套接字服务器位于同一个域的 HTTP 服务器的端口 80 上
如果提供的跨域策略文件与主套接字连接位于同一端口,或者位于不同端口,则通过在跨域策略文件中使用 to-ports 属性来枚举允许的端口,如下例所示:
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy
SYSTEM "http://www.loach.net.cnxml/dtds/cross-domain-policy.dtd">
<!-- Policy file for xmlsocket://socks.mysite.com -->
<cross-domain-policy>
<allow-access-from domain="*" to-ports="507" />
<allow-access-from domain="*.example.com" to-ports="507,516" />
<allow-access-from domain="*.example.org" to-ports="516-523" />
<allow-access-from domain="adobe.com" to-ports="507,516-523" />
<allow-access-from domain="192.0.34.166" to-ports="*" />
</cross-domain-policy>
要检索与主套接字连接位于相同端口中的套接字策略文件,只需调用 Socket.connect() 或 XMLSocket.connect() 方法;并且,如果指定的域与执行调用的 SWF 文件所在的域不同,Flash Player 将自动尝试从正在尝试的主连接所在的相同端口中检索策略文件。要从与主连接位于同一服务器上的不同端口检索套接字策略文件,需使用特殊的"xmlsocket"语法调用 Security.loadPolicyFile() 方法,如下所示:
Security.loadPolicyFile("xmlsocket://server.com:2525");
先调用 Security.loadPolicyFile() 方法,然后再调用 Socket.connect() 或 XMLSocket.connect() 方法。Flash Player 随后将一直等待完成策略文件请求,之后再决定是否允许主连接。
如果要实现套接字服务器,并且需要提供套接字策略文件,则应决定是使用接受主连接的同一端口提供策略文件,还是使用不同的端口来提供策略文件。无论是哪种情况,服务器均必须等待客户端的第一次传输之后再决定是发送策略文件还是建立主连接。当 Flash Player 请求策略文件时,它始终会在建立连接后传输以下字符串:
<policy-file-request/>
服务器收到此字符串后,即会传输该策略文件。程序对于策略文件请求和主连接并不会使用同一连接,因此应在传输策略文件后关闭连接。如果不关闭连接,Flash Player 将关闭策略文件连接,之后重新连接以建立主连接。
发送数据
当 SWF 文件中的 ActionScript 代码向服务器或资源发送数据时,将会发生数据发送操作。对于网络域 SWF 文件,始终允许发送数据。本地 SWF 文件则只有在位于受信任的本地沙箱或只能与远程内容交互的沙箱中时,才能向网络地址发送数据。
可以使用 flash.net.sendToURL() 函数向 URL 发送数据。还可以使用其它方法向 URL 发送请求。这些方法包括 Loader.load() 和 Sound.load() 等加载方法以及 URLLoader.load() 和 URLStream.load() 等数据加载方法。
上载和下载文件
FileReference.upload() 方法可以将用户选择的文件上载到远程服务器。必须先调用 FileReference.browse() 或 FileReferenceList.browse() 方法,然后再调用 FileReference.upload() 方法。
调用 FileReference.download() 方法可打开一个对话框,用户可以在该对话框中从远程服务器下载文件。
如果服务器要求用户身份验证,则只有在浏览器中运行的 SWF 文件(即使用浏览器插件或 ActiveX 控件的文件)才可以提供对话框来提示用户输入用户名和密码以进行身份验证,并且只适用于下载。Flash Player 不允许上载到需要用户身份验证的服务器。
如果执行调用的 SWF 文件位于只能与本地文件系统内容交互的沙箱中,则不允许执行上载和下载操作。
默认情况下,SWF 文件不会在自身所在服务器之外的服务器上执行上载或下载操作。如果其它服务器提供跨域策略文件向执行调用的 SWF 文件所在的域授予访问权限,则执行调用的 SWF 文件可以在其它服务器上执行上载或下载操作。
从导入到安全域的 SWF 文件加载嵌入内容
当加载 SWF 文件时,可以设置用于加载文件的 Loader 对象的 load() 方法中的 context 参数。此参数是一个 LoaderContext 对象。将此 LoaderContext 对象的 securityDomain 属性设置为 Security.currentDomain 时,Flash Player 将在被加载 SWF 文件所在的服务器上检查是否存在跨域策略文件。如果存在跨域策略文件,并且该文件向执行加载的 SWF 文件所在的域授予访问权限,则可以作为导入媒体加载 SWF 文件。这样,执行加载的文件可以获得对 SWF 文件的库中对象的访问权限。
SWF 文件访问其它安全沙箱中被加载 SWF 文件的类的另一种方法是:使被加载的 SWF 文件调用 Security.allowDomain() 方法,以向执行调用的 SWF 文件所在的域授予访问权限。可以将对 Security.allowDomain() 方法的调用添加到被加载 SWF 文件的主类的构造函数方法中,然后使执行加载的 SWF 文件添加事件侦听器,以便响应由 Loader 对象的 contentLoaderInfo 属性调度的 init 事件。当调度此事件时,被加载的 SWF 文件已经调用构造函数方法中的 Security.allowDomain() 方法,因此被加载 SWF 文件中的类可用于执行加载的 SWF 文件。执行加载的 SWF 文件可以通过调用 Loader.contentLoaderInfo.applicationDomain.getDefinition() 从被加载的 SWF 文件中检索类。
处理旧内容
在 Flash Player 6 中,用于某些 Flash Player 设置的域基于 SWF 文件所在的域的末尾部分。这些设置包括对摄像头和麦克风访问权限、存储配额及永久共享对象存储的设置。
如果 SWF 文件所在的域包含的段数超过两个(如 www.loach.net.cn,则会去除该域的第一段 (www),并使用该域的剩余部分。因此,在 Flash Player 6 中,www.loach.net.cn和 store.example.com 都使用 example.com 作为这些设置的域。同样,www.loach.net.cnuk 和 store.example.co.uk 都使用 example.co.uk 作为这些设置的域。这样会导致出现问题,使得来自不相关域(如 example1.co.uk 和 example2.co.uk)的 SWF 文件可以访问相同的共享对象。
在 Flash Player 7 和更高版本中,默认情况下会根据 SWF 文件所在的精确域来选择播放器设置。例如,来自 www.loach.net.cn的 SWF 文件会对 www.loach.net.cn使用一组播放器设置,而来自 store.example.com 的 SWF 文件会对 store.example.com 使用单独的一组播放器设置。
在使用 ActionScript 3.0 编写的 SWF 文件中,当 Security.exactSettings 设置为 true(默认值)时,Flash Player 将针对精确域使用播放器设置。当设置为 false 时,Flash Player 将使用 Flash Player 6 中所用的域设置。如果要更改 exactSettings 的默认值,则必须在需要 Flash Player 选择播放器设置的任何事件(例如使用摄像头或麦克风,或者检索永久共享对象)发生之前进行更改。
如果发布了版本 6 的 SWF 文件并通过该版本创建了永久共享对象,则要从使用 ActionScript 3.0 编写的 SWF 中检索这些永久共享对象,必须先将 Security.exactSettings 设置为 false,然后再调用 SharedObject.getLocal()。
设置 LocalConnection 权限
使用 LocalConnection 类可以开发可以互相发送指令的 SWF 文件。LocalConnection 对象只能在运行于同一台客户端计算机上的 SWF 文件之间通信,但这些 SWF 文件可以在不同的应用程序中运行。例如,一个 SWF 文件在浏览器中运行,而一个 SWF 文件在放映文件中运行。
对于每一次 LocalConnection 通信,都存在一个发送方 SWF 文件和一个侦听器 SWF 文件。默认情况下,Flash Player 允许在同一域中的 SWF 文件之间进行 LocalConnection 通信。对于不同沙箱中的 SWF 文件,侦听器必须通过使用 LocalConnection.allowDomain() 方法来允许发送方具有访问权限。作为参数传递到 LocalConnection.allowDomain() 方法的字符串可以包含以下任意项:确切域名、IP 地址和通配符 *。
allowDomain() 方法的格式已更改,与其在 ActionScript 1.0 和 2.0 中的格式不同。在这两个早期版本中,allowDomain() 是可以实现的回调方法。在 ActionScript 3.0 中,allowDomain() 则是调用的 LocalConnection 类的内置方法。由于此更改,allowDomain() 的用法与 Security.allowDomain() 基本相同。
SWF 文件可以使用 LocalConnection 类的 domain 属性确定其所在的域。
控制对主机网页中脚本的访问
通过使用以下 ActionScript 3.0 API 可实现外出脚本访问:
flash.system.fscommand() 函数
flash.net.navigateToURL() 函数(当指定 navigateToURL("javascript: alert('Hello from Flash Player.')" 等脚本访问语句时)
flash.net.navigateToURL() 函数(当 window 参数设置为"_top"、"_self"或"_parent"时)
ExternalInterface.call() 方法
对于本地运行的 SWF 文件,仅当 SWF 文件和包含该文件的网页(如果存在)位于受信任的本地安全沙箱中时,才能成功调用这些方法。如果内容位于只能与远程内容交互的沙箱或只能与本地文件系统内容交互的沙箱中,则对这些方法的调用将失败。
HTML 代码中用于加载文件的 AllowScriptAccess 参数控制能否从 SWF 文件内执行外出脚本访问。
在 HTML 代码中为承载 SWF 文件的网页设置此参数。可以在 PARAM 或 EMBED 标签中进行设置。
AllowScriptAccess 参数可以有 "always"、"sameDomain" 和 "never" 这三个可能值中的一个:
当 AllowScriptAccess 为"sameDomain"时,仅当 SWF 文件和网页位于同一域中时才允许执行外出脚本访问。这是 AVM2 内容的默认值。
当 AllowScriptAccess 为"never"时,外出脚本访问将始终失败。
当 AllowScriptAccess 为"always"时,外出脚本访问将始终成功。
如果未在 HTML 页面中为 SWF 文件指定 AllowScriptAccess 参数,则默认为 AVM2 内容的"sameDomain"。
AllowScriptAccess 参数可以防止从一个域中承载的 SWF 文件访问来自另一个域的 HTML 页面中的脚本。对从另一个域承载的所有 SWF 文件使用 AllowScriptAccess="never" 可以确保位于 HTML 页面中的脚本的安全性。
共享对象
Flash Player 提供使用"共享对象"的功能,这些对象是永久位于 SWF 文件外部的 ActionScript 对象,它们或者位于用户的本地文件系统中,或者位于远程 RTMP 服务器上。共享对象与 Flash Player 中的其它媒体相似,也划分到安全沙箱中。但是,共享对象的沙箱模型稍有不同,因为共享对象不是可以跨域边界访问的资源,而是始终从共享对象存储区获得的资源,该存储库特定于调用 SharedObject 类的方法的每个 SWF 文件的域。通常,共享对象存储区比 SWF 文件所在的域更精确:默认情况下,每个 SWF 文件使用特定于其整个源 URL 的共享对象存储区。
SWF 文件可以使用 SharedObject.getLocal() 和 SharedObject.getRemote() 方法的 localPath 参数,以便使用仅与其部分 URL 关联的共享对象存储区。这样,SWF 文件可以允许与其它 URL 的其它 SWF 文件共享。即使将 '/' 作为 localPath 参数传递,仍然会指定特定于其自身所在域的共享对象存储区。
用户可通过使用"Flash Player 设置"对话框或"设置管理器"来限制共享对象访问。默认情况下,可以将共享对象创建为每个域最多可以保存 100 KB 的数据。管理用户和用户还可限制写入文件系统的能力。
可以通过为 SharedObject.getLocal() 方法或 SharedObject.getRemote() 方法的 secure 参数指定 true 来指定共享对象是安全的。请注意有关 secure 参数的以下说明:
如果此参数设置为 true,则 Flash Player 将创建一个新的安全共享对象或获取一个对现有安全共享对象的引用。此安全共享对象只能由通过 HTTPS 传递的 SWF 文件来读取或写入,SWF 文件将调用 SharedObject.getLocal() 并将 secure 参数设置为 true。
如果此参数设置为 false,则 Flash Player 将创建一个新的共享对象或获取一个对现有共享对象的引用,后者可由通过非 HTTPS 连接传递的 SWF 文件来读取或写入。
如果执行调用的 SWF 文件不是来自 HTTPS URL,则为 SharedObject.getLocal() 方法或 SharedObject.getRemote() 方法的 secure 参数指定 true 会导致 SecurityError 异常。
对共享对象存储区的选择基于 SWF 文件的源 URL。即使在导入加载和动态加载这两种情况下也是如此,这时 SWF 文件不是源自简单的 URL。导入加载是指在 LoaderContext.securityDomain 属性设置为 SecurityDomain.currentDomain 时加载 SWF 文件。在这种情况下,被加载的 SWF 文件将具有一个伪 URL,该 URL 以执行加载的 SWF 文件所在的域开头,后跟实际的源 URL。动态加载是指使用 Loader.loadBytes() 方法加载 SWF 文件。在这种情况下,被加载的 SWF 文件将具有一个伪 URL,该 URL 以执行加载的 SWF 文件的完整 URL 开头,后跟一个整数 ID。在导入加载和动态加载这两种情况下,都可以使用 LoaderInfo.url 属性检查 SWF 文件的伪 URL。选择共享对象存储区时,可将该伪 URL 完全视为真实的 URL。可以指定使用部分或全部伪 URL 的共享对象 localPath 参数。
用户和管理员可以选择禁止使用"第三方共享对象"。当在 Web 浏览器中执行的任何 SWF 文件的源 URL 与浏览器地址栏中显示的 URL 属于不同的域时,该 SWF 文件可以使用这样的共享对象。用户和管理员可以出于隐私原因选择禁止使用第三方共享对象,从而可以避免跨域跟踪。为避开这种限制,可能希望确保使用共享对象的任何 SWF 文件都只在 HTML 页面结构内部进行加载,这样可以确保 SWF 文件位于浏览器的地址栏中所示的同一个域中。试图使用第三方 SWF 文件中的共享对象时,如果第三方共享对象禁止使用,则 SharedObject.getLocal() 和 SharedObject.getRemote() 方法将返回 null。
摄像头、麦克风、剪贴板、鼠标和键盘访问
当 SWF 文件试图使用 Camera.get() 或 Microphone.get() 方法访问用户的摄像头或麦克风时,Flash Player 将显示一个"隐私"对话框,用户可以在该对话框中允许或拒绝对其摄像头或麦克风的访问。用户和管理用户还可以通过 mms.cfg 文件、"设置 UI"和"设置管理器"中的控制基于站点或全局禁用摄像头访问。用户加以限制后,Camera.get() 和 Microphone.get() 方法均返回 null 值。可以使用 Capabilities.avHardwareDisable 属性确定是已经通过管理方式禁止了 (true) 对摄像头和麦克风的访问,还是允许 (false) 对摄像头和麦克风的访问。
System.setClipboard() 方法允许 SWF 文件用纯文本字符串替换剪贴板内容。这不会带来任何安全性风险。为避免因剪切或复制到剪贴板的密码和其它敏感数据所带来的风险,并未提供相应的"getClipboard"(读取)方法。
Flash 应用程序仅可以监视在其焦点以内发生的键盘和鼠标事件,无法检测其它应用程序中的键盘或鼠标事件。

二 : IDL入门教程

IDL入门教程

安徽遥感考古工作站藏书

1

IDL IDL入门教程

第一章 起步篇...........................................................................................................9

本章概述................................................................................................................................9

书写本书的背景....................................................................................................................9

运用本书..............................................................................................................................10

IDL所需的版本............................................................................................................................10 IDL运行期间所需颜色的数量.....................................................................................................10

本书的风格习惯............................................................................................................................11 本书中所用的IDL程序和数据文件.............................................................................................13 获取更多的帮助...........................................................................................................................15

使用IDL命令.......................................................................................................................15

IDL命令解析................................................................................................................................15 创建变量.......................................................................................................................................18 使用IDL图形窗口........................................................................................................................23

第二章 简单的图形显示.........................................................................................26

本章概述..............................................................................................................................26

IDL中简单的图形显示.......................................................................................................26

创建线画图..........................................................................................................................26

定制线画图..........................................................................................................................29

改变线条的线型和粗细...............................................................................................................29 用符号代替线条显示数据...........................................................................................................29 用不同的颜色绘制线画图...........................................................................................................32 限定线画图的范围.......................................................................................................................32 改变线画图的风格.......................................................................................................................33

在线画图上绘出多种数据集..............................................................................................34

在多个轴的图上显示数据...........................................................................................................36

创建曲面图..........................................................................................................................36

定制曲面图..........................................................................................................................39

旋转曲面图...................................................................................................................................39 为曲面赋色...................................................................................................................................40 修改曲面图外观...........................................................................................................................41

创建阴影曲面图..................................................................................................................42

改变阴影处理参数.......................................................................................................................42 用其它数据集为阴影处理提供参数...........................................................................................43

创建等值线图......................................................................................................................44

选择等值线数目..................................................................................................................46

扩展:idl / idl教程 / idl程序设计

修改等值线图......................................................................................................................47

改变等值线图的外观...................................................................................................................48 给等值线图赋色...........................................................................................................................49 2

IDL IDL入门教程

创建填充的等值线图..........................................................................................................50

在显示窗口定位图形输出..................................................................................................51

设置图形边缘...............................................................................................................................52 设置图形位置...............................................................................................................................53 设置图形区域...............................................................................................................................54 创建多个图形...............................................................................................................................54

给图形显示添加文本..........................................................................................................58

找出可用字体的名称...................................................................................................................59 用XYOutS命令添加文本.............................................................................................................59 用矢量字体使用XYOut...............................................................................................................60 排列文本.......................................................................................................................................61 删除文本.......................................................................................................................................61 改变文本的方向...........................................................................................................................62

给图形显示添加线和符号..................................................................................................62

图形显示添加色彩..............................................................................................................63

第三章 图像数据处理.............................................................................................72

本章概要..............................................................................................................................72

图像处理..............................................................................................................................72

显示图像.......................................................................................................................................72 调整图像数据...............................................................................................................................74 显示24位图像.............................................................................................................................76 控制图像显示顺序.......................................................................................................................77 改变图像尺寸...............................................................................................................................77 在显示窗口中定位图像...............................................................................................................78 从显示器中读取图像...................................................................................................................81

IDL中基本的图像处理.......................................................................................................82

直方图均衡化...............................................................................................................................82 平滑图像.......................................................................................................................................66 增强图像棱边...............................................................................................................................68 图像的频域滤波...........................................................................................................................68

第四章 图形显示技术.............................................................................................84

本章概要..............................................................................................................................84

IDL的颜色运用...................................................................................................................84

扩展:idl / idl教程 / idl程序设计

使用索引颜色模式和RGB颜色模式...........................................................................................84 在24位显示设备上装载色谱表.................................................................................................89 获得色谱表的拷贝.......................................................................................................................89 修改和创建色谱表.......................................................................................................................90 保存自己的色谱表.......................................................................................................................91

创建自己的轴标注..............................................................................................................92

调整轴刻度间隔...........................................................................................................................92 3

IDL IDL入门教程

格式化轴的标注...........................................................................................................................93

用IDL处理残缺的数据.......................................................................................................96

用IDL建立三维坐标系.......................................................................................................98

建立三维散点图...........................................................................................................................98 从图形原点定位3D坐标轴........................................................................................................100 组合简单图形显示............................................................................................................101 IDL中的动画数据.............................................................................................................103 建立动画工具..............................................................................................................................104 装载动画缓冲区..........................................................................................................................104 运行动画工具..............................................................................................................................104 动画的控制..................................................................................................................................104 存储动画的像素映射图..............................................................................................................105 其它类型图形数据的动画..........................................................................................................105 网格化数据以便图形显示................................................................................................106 德洛内三角形法网格化..............................................................................................................107 数据的球形网格化......................................................................................................................109

第五章 图形显示技巧...........................................................................................111 本章概要............................................................................................................................111 将光标用于图形显示........................................................................................................111 什么时候返回的光标位置?......................................................................................................111 哪一个鼠标键和光标共同作用呢?..........................................................................................112 用光标标注图形输出..................................................................................................................112 在图像上使用Cursor命令...........................................................................................................113 在循环中使用Cursor命令...........................................................................................................114 从显示中删除注释............................................................................................................115 删除注释的异或法......................................................................................................................115 删除注释的设备拷贝法..............................................................................................................117 Z图形缓冲区中的图形显示技巧.....................................................................................121 Z图形缓冲区的实现....................................................................................................................121 一个Z图形缓冲区实例:两个曲面............................................................................................122 用Z图形缓冲区使图像变形........................................................................................................124 Z图形缓冲区中的透明效果........................................................................................................127 将Z图形缓冲区效果与体数据着色相结合................................................................................128

扩展:idl / idl教程 / idl程序设计

第六章 在IDL中读写数据......................................................................................129 本章概要............................................................................................................................129 打开文件进行读写............................................................................................................130 查找和选择数据文件..................................................................................................................130 获取逻辑设备号..........................................................................................................................132 读写格式化数据................................................................................................................133 4

IDL IDL入门教程

写自由格式文件..........................................................................................................................133 读写自由格式文件的实例..........................................................................................................136 用确定的文件格式写入..............................................................................................................140 从字符串中读取格式数据..........................................................................................................141 读写非格式化数据............................................................................................................141 读取非格式化图像数据文件......................................................................................................142 写非格式化图像数据文件..........................................................................................................142 非格式化数据文件的一些问题..................................................................................................144 用关联变量存取非格式化数据文件..........................................................................................144 读写常用文件格式的文件................................................................................................147 创建彩色GIF文件........................................................................................................................147 创建彩色JPEG文件.....................................................................................................................148 查询图像文件信息......................................................................................................................150

第七章 图形硬拷贝输出.........................................................................................151 本章概要............................................................................................................................151 选择图形硬拷贝输出设备................................................................................................151 配置图形硬拷贝输出设备................................................................................................152 常用的Device命令关键字...........................................................................................................153 创建PostScript文件......................................................................................................................154 将图形送到硬拷贝设备中................................................................................................154 打印PostScript文件..........................................................................................................155 在运行MacOS系统的计算机上打印PostScript文件..................................................................156 在Windows计算机上打印PostScript文件...................................................................................156 生成封装的PostScript文件输出......................................................................................156 封装PostScript图形的预览..........................................................................................................157 生成彩色的PostScript输出..............................................................................................157 PostScript中的彩色图像与灰度图像..........................................................................................158 在PostScript设备上创建高质量的输出..........................................................................158 显示设备和PostScript设备之间的相同点..................................................................................159 显示设备与PostScript设备之间的不同点..................................................................................159 在横向输出模式中计算PostScript的偏移量..................................................................171 用PS_Form配置PostScript设备......................................................................................172 配置和使用打印设备........................................................................................................173 用打印设备定位图形..................................................................................................................174

扩展:idl / idl教程 / idl程序设计

第八章......................................................................................................IDL编程基础 188 本章概述............................................................................................................................188 编写IDL批处理文件.........................................................................................................188 5

IDL IDL入门教程

编写IDL主程序.................................................................................................................189 过程和与函数中变量的作用范围..............................................................................................191 创建定位参数..............................................................................................................................191 定义可选的或必须的定位参数..................................................................................................192 定义关键字..................................................................................................................................193 创建输出型参数..........................................................................................................................196 编写IDL函数.....................................................................................................................198 方括号和函数的调用..................................................................................................................200 使用程序控制语句............................................................................................................201 IDL中表达式的真和假...............................................................................................................201 将多个语句处理成单个语句......................................................................................................201 If…Then…Else控制语句............................................................................................................202 条件表达式..................................................................................................................................203 FOR循环控制语句......................................................................................................................203 WHILE循环控制语句.................................................................................................................204 REPEAT...UNTIL 循环控制语句...............................................................................................204 CASE控制语句............................................................................................................................204 GOTO控制语句...........................................................................................................................205 错误处理控制语句......................................................................................................................205 编译和执行IDL程序模块.................................................................................................207 程序编译规则:..........................................................................................................................208 程序编译和自动运行规则..........................................................................................................208 特殊编译命令..............................................................................................................................209

第九章..................................................................................................编写 IDL 程序 177 本章概述............................................................................................................................177 基本的ImageBar程序.......................................................................................................177 给程序ImageBar增加一个“先擦除”功能...............................................................................181 向ImageBar程序增加颜色敏感功能...........................................................................................181 给ImageBar中的命令传递关键字..............................................................................................183 根据窗口大小改变字符大小......................................................................................................185 程序ImageBar的最终代码..........................................................................................................186 在图形用户界面中包装ImageBar..............................................................................................187

第十章.........................................................................................编写简单的组件程序 230 本章概述............................................................................................................................230 组件程序的结构................................................................................................................230 组件程序如何对事件作出反应........................................................................................231 编写组件定义模块............................................................................................................231 定义和创建程序组件..................................................................................................................232 在屏幕上实现组件......................................................................................................................233 使绘图组件成为当前图形窗口..................................................................................................234 6

扩展:idl / idl教程 / idl程序设计

IDL IDL入门教程

在绘图组件窗口上显示图形......................................................................................................234 保存程序运行时所需要的信息..................................................................................................234 创建事件循环和注册程序..........................................................................................................235 运行程序......................................................................................................................................236 创建无阻塞组件程序..................................................................................................................236 编写事件处理模块............................................................................................................237 事件结构中的公共字段..............................................................................................................237 事件处理函数..............................................................................................................................238 将事件处理程序和组件联系起来..............................................................................................239 编写Quit按钮的事件处理程序...................................................................................................240 编写改变图形窗口大小的事件处理程序..................................................................................241 进行小量地修改................................................................................................................242 添加颜色敏感..............................................................................................................................242 采用更高效的内存管理..............................................................................................................243

第十一章.................................................................................................组件编程技巧 210 本章概述............................................................................................................................210 改变颜色表........................................................................................................................210 保护公共块..................................................................................................................................211 一个可选择颜色表的工具..........................................................................................................211 指定Group Leader........................................................................................................................214 给组件程序增加Group Leader....................................................................................................215 在24位显示器上改变颜色表....................................................................................................215 在组件程序中使用指针....................................................................................................217 使用Cleanup过程防止内存泄露.................................................................................................219 使用伪事件进行程序通信................................................................................................220 创建一个具有“记忆功能”的程序..........................................................................................221 保护组件程序的颜色........................................................................................................223 通过组件跟踪事件来保护颜色..................................................................................................224 通过绘图组件事件来保护颜色..................................................................................................225 保存或者发布程序的图形................................................................................................226

第十二章.....................................................................................................对话框程序 247 本章概述............................................................................................................................247 创建模式对话框................................................................................................................247 阻塞的组件程序..........................................................................................................................247 模式组件程序..............................................................................................................................248 编写模式对话框的定义模块......................................................................................................248 编写模式对话框的事件处理模块..............................................................................................252 测试模式对话框程序..................................................................................................................253 创建非模式的对话框........................................................................................................253 7

扩展:idl / idl教程 / idl程序设计

IDL IDL入门教程

编写非模式对话框程序..............................................................................................................254 编写非模式对话框的事件处理模块..........................................................................................256 测试非模态对话程序..................................................................................................................257

附录A..................................................................................................组件的事件结构 258 事件结构的定义................................................................................................................258 公共字段的定义..........................................................................................................................258 基本组件的事件结构........................................................................................................259 base组件.......................................................................................................................................259 按钮组件......................................................................................................................................259 绘图组件......................................................................................................................................259 下拉式列表组件..........................................................................................................................259 标签组件......................................................................................................................................260 列表组件......................................................................................................................................260 滑动条组件..................................................................................................................................260 表单组件......................................................................................................................................260 文本组件......................................................................................................................................262 复合组件的事件结构........................................................................................................262 CW_Animate................................................................................................................................262 CW_Arcball..................................................................................................................................262 CW_BGroup.................................................................................................................................262 CW_Clr_Index.............................................................................................................................263 CW_Color_Sel.............................................................................................................................263 CW_DefROI.................................................................................................................................263 CW_Field.....................................................................................................................................263 CW_Form.....................................................................................................................................263 CW_Flisder..................................................................................................................................263 CW_Orient...................................................................................................................................263 CW_PDMenu...............................................................................................................................264 CW_RGBSlider............................................................................................................................264 CW_Zoom....................................................................................................................................264 组件程序的事件结构........................................................................................................264 Xcolors.........................................................................................................................................264 其他组件的事件结构........................................................................................................264 键盘焦点事件..............................................................................................................................264 组件退出请求事件......................................................................................................................265 组建计时器事件..........................................................................................................................265 组件跟踪事件..............................................................................................................................265

扩展:idl / idl教程 / idl程序设计

附录B.......................................................................................................数据文件描述 266 8

IDL IDL入门教程

第一章 起步篇

本章概述

本章意在解释写这本书的目的,通过阅读本书能学到什么,以及为读者提供一些能使读者更方便地使用本书中IDL编程例子的信息。[www.loach.net.cn]将学会如下几点:

1. 本书是如何组织的。

2. 怎样使用本书。

3. 如何下载和组织随本书附带的文件。

4. 如何使用IDL的变量,关键字和命令。

5. 如何创建和运行IDL的矢量和数组。

6. 如何使用IDL的图形窗口。

书写本书的背景

本书是在多年来教科学家和工程师使用和操作IDL(Interactive Data Language)的基础上创作的,而且教学的绝大部分时间是为IDL的开发者Research Systems公司工作。当笔者在回答一个又一个问题之后,笔者意识到多数问题属于一些同类问题。事实是,多数人想用IDL做许多同样的事情。想做的是分析和演示数据,写出高效率的程序来解决科学问题,并且最主要的是快速做完工作。多数人并不想做的事情是阅读计算机软件教科书。IDL是一套大型软件并且在不但壮大。随之而来的是大量的文档资料,笔者知道没有人愿意去读这些资料。如果让某人独自开始学习IDL的奥秘,IDL将是件可怕的事情,甚至对有经验的用户来说也是一样。本书意在使读者掌握IDL,教给读者在日常运行IDL所必需知识的80%。更为重要的是,本书的例子使IDL更容易理解。无论如何,本书将演示如何使用IDL。

本书的读者是IDL初学者,特别是哪些不得不自学IDL的读者。学好IDL需要很长的阶段。多数人不能利用工作中的时间学习IDL,笔者想写一本能满足这两类人学习IDL的书。总之,本书为不喜欢读教科书并能通过例子学得最好的人全面介绍IDL的精髓。本书在IDL编程技术和技巧方面只做了简要概略,而这些技术只能通过练习获取。最根本的是,这是一本笔者在学习IDL时所期望的书。

9

IDL IDL入门教程

运用本书

笔者曾试图使本书每章能具有独立性,这样能拿起本书就可翻到任何一章去学习最需要的知识。[www.loach.net.cn)但在安排章节时,或多或少是根据笔者在IDL教学时的顺序安排。如果刚开始学IDL,那么按照书中的顺序从头开始学完本书将更合理。书中后面的几章编程教程是建立在前面几章中讲过的概念和技巧的基础上的。

IDL所需的版本

希望读者在学习本书时使用的是最新的IDL版本。本书写作时使用的是IDL5.2版。使用较早版本可以使用本书中编程例子的大部分,但笔者没有试图使本书中的例子程序与较早的IDL软件版本兼容。特别是,较早版本的用户在使用长文件名(如果在Windows环境下)、指针(必须用句柄代替它)以及方括号来引用数组下标时(必须用圆括号代替它)时存在困难。如果需要升级软件,可以在Research Systems公司的WWW网址 http://www.rsinc.com/上查找关于Research Systems公司和当地IDL代理商的信息,包括如何升级软件的信息。

IDL运行期间所需颜色的数量

书中程序例子是按IDL在256种颜色模式下运行编写的,使用通常称为索引颜色的模式(详细细节参考83页的“使用IDL的颜色”章节)。这意味着所显示的颜色是索引号或是与色彩表相连的颜色,这样在色彩表中的颜色变化时,所显示的颜色也一同变化。启动IDL并在IDL命令行键入如下IDL命令,能发现所用的颜色模式。

IDL>Window

IDL>Print, !D.N_Colors

当!D.N_Colors的值大于或等于256时,仍然能够使用书本中的例子,但必需对代码做一点改变。大多数人使用的颜色值都小于256。比较典型的颜色值介于200与245之间。笔者推测在本书中至少要用150种颜色。那就是说,!D.N_Colors的值应在150至256之间。 少于150种颜色会怎样?

如果在IDL运行中少于150种颜色,并且计算机运行在公用桌面环境(CDE),可以将CDE环境下的颜色数设置为不是“高”的那种。设置为“中”或“低”的情况下,程序将运行良好。在视窗环境操作手册的在线帮助中查找如何改变这个设置。

如果不是用的共用桌面环境,颜色数也少于150,并且不是在PC机或Macintosh计算机上运行IDL的话,那么很可能运行了其它应用程序,该应用程序使用了要分配给IDL的颜色值。网页浏览器很可能就是这样的应用程序。退出当前任务,重新登录,并在重新登录后最先启动IDL。键入以上命令,如果仍然得到少于150种的颜色,那么需要联系Research Systems公司的技术人员,以获取更多的帮助。

如果颜色数少于150种,并且是在PC或Macintosh计算机上运行的IDL,那么,检查显卡以确保设置为256色。一般可通过显示器的控制面板完成。详细细节参考计算机文档资料。 10

IDL IDL入门教程

多于256种颜色将会怎么样呢?

如果在IDL运行中多于256种颜色,并且IDL是运行在X Window环境下的计算机上,就可让IDL使用8位的伪彩色显示级别。(www.loach.net.cn)

退出IDL,并重新启动IDL。在做任何操作之前,键入以下命令:

IDL>Device,Pseudo_Color=8.Decomposed=0

为了确认是在使用8位伪彩色显示级别,键入:

IDL>Help, /Device

所显示的信息使读者确信使用的是伪彩色显示级别,并且所使用的颜色数为256或少于256。如果想使用本书中的例子,每次进入IDL时都需要键入DEVICE命令。可以将此命令放在IDL启动文件中。查看IDL文档资料以获取更多的详细资料。

如果在IDL运行中多于256种颜色,并且是在PC或Macintosh计算机上运行IDL,需要检查显示卡的设置参数以确保设置为256色。一般通过显示器或显示面板完成。详细细节参考计算机文档资料。修改参数后必须重新启动IDL。

如果喜欢在16位或24位的颜色模式(在Macintosh和PC计算机上只支持16位,而且如果是24位也将作为16位处理)中工作,那么键入以下命令以确保颜色分解已被关闭: IDL>Device, Get_Visual_Depth=thisDepth

IDL>IF thisDepth GT 8 THEN Device, Decomposed=0

如果在这种模式下对颜色表做些修改,记住这些修改不会在显示窗口中立即更新。必须在显示窗口中刷新图形以查看这些颜色改变是否起作用。详细细节参考83页的“IDL的颜色运用”章节。

扩展:idl / idl教程 / idl程序设计

创建IDL的启动文件

记住,每次启动IDL来使用本书的命令时,都必须执行以上命令。为此,可以将这些命令输入IDL 的启动文件中。当每次IDL启动时,启动文件中的命令都被执行,这就像在IDL命令提示符下键入这些命令。为了解如何在使用的计算机中创建IDL开始文件,可在IDL命令行键入以下命令,以获取在线帮助:

IDL>? Startup

本书的风格习惯

笔者尽量用统一的风格贯穿全书,这样不会被本书文字的功能和目的所迷惑。首先,在IDL命令行或IDL编辑器窗口所键入的命令总是以Courier字体形式来书写:

Surface, data

在IDL命令行键入的命令都显示在IDL提示符“IDL>”的后面:

IDL>Surface, data

其它的IDL命令都是在文本编辑窗口键入的。可以选择自己的文本编辑器或使用IDL提供的文本编辑器,这由读者决定。

11

IDL IDL入门教程

大写

在本书中,用大写这种形式来书写IDL命令。[www.loach.net.cn]这种形式完全是任意的。 IDL对字母的大小写不敏感,但与操作系统打交道的命令(例如:UNIX操作系统对IDL所打开的文件名的大小写敏感)和执行字符串比较命令时除外。大写可以有助于记住命令和关键字名,并且一目了然地知道命令行中哪些单词是函数名。

所有IDL命令和关键字的第一个字母大写。此外,任何有助于记忆的字母也用大写。例如: Surface, data, charsize=2.0, Color=180

XLoadCT

Widget_Control, tlb, Set_UValue=info, /No_Copy

变量名的第一个字母没有用大写字母,但是当变量名中的字母有可能构成单词时使用大写。例如: data=FIndGen(11) buttonValue=thisValue

ptrToData=Ptr_New()

IDL的保留字全部用大写字母,例如:

REPEAT test UNTIL

FOR j=0,10 DO BEGIN

ENDWHILE

在IDL命令行或文本编辑器上,当键入命令时,可以随意使用大写字母。

注释

在IDL命令中,分号右边的任何文本都被视为是注释,IDL解释器将忽略它。简言之,可在IDL的程序中写入注释。通常在分号的前后加上空格,并让注释行缩进三个空格。例如: ; This is the loop part of the program.

FOR j=0,10 DO BEGIN

data=j*2

count=count +j

ENDFOR

偶尔,会在命令行的末端看到一个注释,这是在定义IDL结构变量的字段时,特别这样做的。例如:

info={r:r,$ ; The red color vector

g:g,$ ; The green color vector

b:b,} ; The blue color vector

12

IDL IDL入门教程

续行符 IDL中的续行符是美元的符号“$”。[www.loach.net.cn]这表示IDL命令延续到下一命令行(见上例)。在本书中将看到很多续行符。建议在IDL命令行中不使用续行符,应该在IDL命令行行输入完整的IDL命令。IDL命令行将忽略续行符。例如,可以用如下方法键入上述命令:

IDL>info={r:r, g:g, b:b}

在出现输入错误或在以后需修改命令时,这将使得重新键入这些命令变得更加简单。 有时需要完全按照书中出现的IDL命令输入。笔者将告知什么情况下这样做。当在IDL命令行想键入For循环时就需要这样做。在命令行中一次键入多行命令是非常聪明的做法。必须让IDL解释器认为这些命令为一个命令。这就需要在IDL的命令行上正确使用行续符($)和多行命令符(&)。

本书中所用的IDL程序和数据文件

当使用这本书时,许多IDL程序和数据文件已经准备就绪。IDL程序文件经常有一个.pro扩展名,数据文件有一个.dat扩展名。还有一些.txt扩展名的文件。这些是文本文件。 安装程序和数据文件

建议创建一个名为coyote的子目录,并把所有的程序,文本,数据文件都放在其中。coyote子目录通常是IDL目录下的一个子目录(让IDL内部的系统变量!Dir指向这个目录),当然它并非一定要在这个目录下,可以在任何地方创建。IDL主目录是另外一个存放这些文件的好地方。当需要这些文件时,不直接在coyote子目录下修改,而是将这些文件拷贝到当前工作目录下是一个好注意。这样就保留了原始的没有修改的文件。

如果没有选择创建一个coyote子目录,那么就将本书提供的程序将默认这些文件已经放在当前目录中。这个目录是一个启动IDL时的目录,或者是在PC或Macintosh计算机上IDL的Preferences对话框中Startup所指定的目录。

获取IDL的主目录和当前目录

如果不知道IDL的主目录是什么。启动IDL,键入以下命令:

IDL>CD, Current=homeDirectory

IDL>Print, homeDirectory

当前目录不一定是主目录。在IDL运行期间,可以用同样的命令随时获得当前目录: IDL>CD, Current=currentDirectory

IDL>Print, currentDirectory

注意,如果按上述做法装载数据文件时遇到问题,请确保是在所希望的目录下。不用IDL主目录(例如:5.2 Windows版的IDL软件中,IDL5.2就是IDL的主目录)作为工作目录可能会是一个好注意,因为这样很容易删除重要文件。

13

IDL IDL入门教程

下载本书所用的程序和数据文件

书中文件可以通过互联网以匿名FTP登录下载。[www.loach.net.cn)如果在使用网络浏览器,进入Coyote's Guide to IDL Programming热连接,网址是:

http://www.dfanning.com/

如果用匿名ftp,文件可以通过网络浏览器在如下网址找到:

ftp://ftp.frii.com/pub/dfanning/outging/coyote

用文本或ASCII模式下载所有的程序和文本文件(例如:那些带.pro或.txt扩展名的文件),用BINARY模式下载所有的数据文件(例如:那些带.dat扩展名的文件)。如果愿意,并且电脑能解压缩zip文件,下载coyotefiles.zip文件就可一次性地将所有的程序、文本文件和数据拷贝下来。

确保Coyote目录在IDL的搜索路径内

无论在什么地方创建coyote目录或储存本书的文件,需要确保这个目录在 IDL搜索路径中。在IDL中,路径用!path系统变量给出。以后将学到更多关于该系统变量的作用,但现在只要知道它是一系列的子目录,当IDL遇到不认识的命令时就这些子目录查找相应的命令。打印该系统变量可以看到当前的IDL搜索路径:

扩展:idl / idl教程 / idl程序设计

IDL>Print, !path

如果使用的是PC机,这些子目录用分号隔开;在Macintosh或VMS机器上,它们用逗号隔开;在UNIX机器上,它们用冒号隔开。

想在IDL搜索路径中添加coyote目录,当IDL的当前目录在coyote目录下时键入AddPath命令即可(如果没有创建coyote目录,可以将IDL的当前路径改变为存放本书文件的目录名,然后键入AddPath命令)。使用CD命令来转换到IDL的当前的目录。例如,如果coyote目录是IDL主目录下的一个子目录,并且这个主目录是当前目录,可以键入如下命令来在IDL的搜索路径中添加coyote目录:

IDL>CD, 'coyote'

IDL>AddPath

如果每次运行IDL时都想进入coyote目录(或本书文件所在的目录)并且运行AddPath程序,也许会想到将该命令添加到IDL启动文件中(详细细节参考第四页的“创建IDL的启动文件”)。或者,想将coyote目录永久性地添加到IDL的搜索路径中。(这取决于使用的操作系统和IDL的配置文件。关于设置!Path系统变量,可参考IDL的在线帮助)

拷贝数据文件

如果愿意,可从计算机上其它地方拷贝本书所用到的IDL数据文件,不必通过匿名的ftp来下载。为此,可使用CopyData命令,这个命令是刚下载的文件之一。进入coyote目录(或书中文件所在的目录),如果使用的是IDL5版,只需键入CopyData:

IDL>CopyData

如果运行的是IDL更早的版本,将通过Demo关键字为CopyData程序提供IDL的演示目录(演示目录名在IDL先前版本中各不相同,而且不一定被安装)。如在PC机上演示目录经常命名为“C:\RSI\IDLDEMO4”。所以应该键入如下命令:

14

IDL IDL入门教程

IDL>CopyData,Demo="C:\RSI\IDLDEMO4"

数据文件将从不同的地方被选出并拷贝到当前目录上。[www.loach.net.cn)本书附有这些数据文件的一个列表,说明了它们的类型和大小。见313页的“附录 B: 数据文件描述”。

获取更多的帮助

当在安装这些程序文件或在IDL编程的其它方面需要帮助时,查看Coyote's Guide to IDL Progamming网页。将找到关于本书和IDL常规编程的信息。如果情况更糟的话,也可以在那里看到一张表格,通过该表格可以直接和笔者联系。Fanning软件顾问和Coyote's Guide to IDLProgramming的网址为:

http://www.dfanning.com/

使用IDL命令

本书是一本实践性很强的书。当阅读它时,笔者宁愿读者坐在电脑前,也不愿读者坐在火炉前。笔者希望读者键入命令并查看发生了什么。为此,本书前半部分的多数命令需要在IDL命令行上键入(如果想保存所键入的命令,可以创建一个日志文件来记下它们。参考第11页的“创建日志命令”)。

随着IDL5.0的问世,IDL慢慢地变得越来越像程序语言。例如,对象图形引擎并不真正地用来在IDL命令行上使用的,而是专门设计用在IDL编程中。但是从命令行键入IDL命令中能学到很多东西。特别是,能学会画出某些东西,测试一些东西,并可用数据文件做实验。称之为“循序渐进”。这是学习IDL的最好方法之一。

下面是刚开始所必需知道的。首先,将看到本书中的许多类似下面的命令:

Contour,peak,lon,lat,XStyle=1,YStyle=1, /Follow,$

Levels=vals,C_Label=[1,0,1,0,0,1,1,0]

如果知道所看到的东西是什么将非常有助于学习。

IDL命令解析 在上面的命令中,单词Contour是IDL命令或所希望运行程序的名字。它必须被完整地拼出。一些命令会很长,但不能缩写。命令行中peak,lon,以及lat是变量。它们可以用来将信息传入或传出命令或程序。XStyle,YStyle,Follow,Levels以及C_Lables为关键字。一般来将关键字对命令来说可选的。如同变量,它们用来将信息传入或传出IDL命令或IDL程序。

定位参数

在以上命令中的三个变量peak,lon,及lat称为定位参数。在这个特殊例子中,这些定位参数为输入变量(例如,它们把数据传入命令),但仅仅看到她们并不能辨认出其是不是输入变量。它们也可以简单地用作输出变量(或者,在某种情况下,它们既可以是输入变量也可以是输出变量)。其命令行语法完全一样。只有通过上下文,通过阅读关于这类命令或程序的公开文档才能辨别。

一个定位参数在命令名的右边有其确定的顺序。(注意,以下讨论的关键字参数不会影响定位 15

IDL IDL入门教程

参数的顺序)。[www.loach.net.cn]在这个例子中,peak变量必须在Contour命令右边,在lon变量的左边。lon变量必须在peak变量的右边,lat变量的左边。不能遗漏第二个参数,只给定第一和第三个定位参数。 例如,下面这两条命令的格式是不正确的并会导致错误。第一条命令的定位参数顺序被改变,

第二条命令遗漏了第二个定位参数。

Contour, lon, peak, lat, XStyle=1, YStyle=1, /Follow, $

Levels=vals, C_Labels=[1,0,1,0,0,1,1,0]

Contour, peak, , lat, XStyle=1, YStyle=1, /Follow, $

Levels=vals, C_Labels=[1,0,1,0,0,1,1,0]

一般情况下,命令的定位参数必须给定参数,但并不总是如此。例如,在上面正确的命令中,peak是Contour命令必需的参数,但是lon和lat是可选定位参数。

关键字参数

XStyle,YStyle,Follow,Level和C_Labels都是关键字参数。与定位参数不同,关键字参数能任何顺序出现在命令名右边。它们甚至能出现在定位参数中间而不影响定位参数之间的相对位置。换句话说,关键字参数不能像定位参数那样对待。以下的Contour命令是个有效构造。 Contour, peak, Level=vals, lon, XStyle=1, YStyle=1, $

/Follow, lat, C_Lavels=[1,0,1,0,0,1,1,0]

一般情况下,关键字参数是可选参数。像定位参数一样,它们也能成为命令的输入变量或输出变量。将通过本书或阅读命令的文档得知这一点。

注意在上列命令中关键字的使用方法。关键字能设置为一个特定值(例如,XStyle=1),一个变量(例如,Levels=vals),一个数组(例如,C_Labels=[1,0,1,0,0,1,1,0]),甚至可以用一个斜杠字符来设定(例如,/Follow)。

注意最后的一条语法。有些关键字有二进制特性。换句话说,它们要么on/off, yes/no, true/false, 1/0,等等。能经常发现这些关键字通过/Keyword这种语法来设置或打开。语法/Keyword等同于语法Keyword=1。

扩展:idl / idl教程 / idl程序设计

事实上,以上Contour命令能被写成这样:

Contour,peak,Levels=vals,lon,/XStyle,/YStyle,$

/Follow,lat,C_Lavels=[1,0,1,0,0,1,1,0]

这个命令和上面的命令是一回事。命令不能写成这样的原因是,它可能错误地暗示了X轴和Y轴关键字有二进制特性,但它们不是,它们能被设置为除0和1以外的其它值。

IDL过程和函数

这个特殊的命令Contour是一个IDL过程。IDL命令要么是过程,像这个命令一样,要么是函数。如下的IDL命令BytScl就是一个函数:

scaled=BytScl(image, Top=199, Min=0, Max=maxValue)

注意Contour过程和BytScl函数的不同。首先,在函数命令中,定位参数和关键字放在一对圆括号中的。在过程命令中,参数和关键字仅排列在一个命令行上。但是,最重要的区别是函数命令显示地返回一个值,等号左边的一个变量用于返回该值。这是IDL中函数命令和过程命令根本的区别。

函数命令总是显示地返回一个值,这个数值必须赋予给一个变量。函数返回值可能是任一种IDL变量,包括数值,数组和结构。在这个例子中,返回值scaled是一个与image定位参数具有 16

IDL IDL入门教程

相同维数的字节型数组。(www.loach.net.cn]

有时将看到一个函数和过程写在一起,例如,考虑一下这两个命令:

scaled=BytScl(image,Top=199,Min=0,Max=maxValue)

TV,scaled

第一个命令是一个函数命令,另一个是过程命令,此过程使用函数的返回值作为其定位参数,两个命令写成如下这样在IDL中很常见:

TV,BytScl(image,Top=199,Min=0,Max=maxValue)

在这种情况下,BytScl命令首先被执行并得到一个返回值,此返回值作为TV命令的定位参数。

花一些时间熟悉各种IDL命令,就能立即识别哪个是过程,哪个是函数,但尽量记住这一点:当正在从一个命令中寻找某个值时,要想到这个命令可能是一个函数。在本书后面中将学会怎样写IDL过程和函数。

用IDL命令帮助

IDL有全面的在线帮助系统,能为读者提供有关IDL命令和参数的非常有帮助的信息。通过在IDL命令行中输入一个问号,或在IDL开发环境下拉菜单中选择Help菜单项目获得在线帮助。IDL文档集中的大部份信息都可通过在线帮助获得。为了获得IDL在线系统帮助,仅仅需要在IDL的命令行中输入一个问号,如下:

IDL>?

创建命令日志

也许希望将在命令行里面输入的命令保存为日志或记录。如果是这样,可创建一个日志文件。日志文件是一个IDL批处理文件(参考205页的“创建IDL批处理文件”)。在IDL中用Journal命令打开一个日志文件,并指定想打开的文件名。该文件将是一个用于写信息的新文件。从IDL命令行不能添加日志文件。例如,为了写一个命名为book_commands.pro的日志文件, 键入: IDL>Journal, 'book_commands'

随后所有在IDL命令行上键入的命令都将写入这个日志文件。

IDL>a=[3,5,7,3,6,9]

IDL>Help, a

IDL>Plot, a

当想关闭日志文件时,再次在IDL命令行键入Journal命令,如下:

IDL>Journal

日志文件是能编辑的一个简单的ASCII文本文件。如果愿意,可用任何一个文本编辑,包括由IDL的PC版本附带的编辑器。当想再次执行日志文件中的命令时,在IDL命令行键入@作为开头字母。例如,要执行在上面book_commands.pro文件中的命令,如下:

IDL>@book_commands

确定创建的每个日志文件有唯一的名称。不能添加日志到这些日志文件,所以,如果第二次建立的日志文件名和第一次相同,许多操作系统将会毫无警告地覆盖第一个日志文件。

如果每次建立日志文件时都想要一个唯一文件名,可用下列的IDL程序完成:

PRO Journal_Unique

Journal, String('journal_',Bin_Data(SysTime()),'.pro',$

17

IDL IDL入门教程

Format='(A,I4,5I2.2,A)') END

然后,用Journal_Unique代替Journal,就可以建立每次都具有唯一文件名的日志文件。(www.loach.net.cn)

创建变量

在这本书中将创建许多变量。如果以前对变量有所了解将会大有益处。变量名必须以字母开头。它们可以包括其它字母,数字,下划线,美元符号。一个变量名最长可达255个字符。本书的习惯是让变量名的首写字母小写。下面是一些有效的变量名: ptrToData image2 this_image a$handle

变量名有两个重要属性:数据类型和组织结构。数据类型指出属于数据类型中的哪一种。在IDL中有14种基本数据类型。在图表1中将看到每一种数据类型,每个类型创建的变量的字节大小,变量创建方式,数据类型之间强制转换的IDL函数名称。除了数据类型外,一个变量有一个组织结构。有效的组织结构有标量(例如单个数值)、矢量(真正的一维数组)、数组(最高可达8维)和IDL结构(能包含各种数据类型的变量和组织结构,结构中独立的组成部分称为字段)。

数据类型 字节型 16位有符号整型 32位有符号长整型 64位有符号整型 16位无符号整型 32位无符号长整型 64位无符号整型

浮点型 双精度浮点型

复数 双精度复数 字符串 指针 对象

字节数 1 2 4 8 2 4 8 4 8 8 16 0-32767

4 4

创建变量 Var=0B Var=0 Var=0L Var=0LL Var=0U Var=0UL Var=0ULL Var=0.0 Var=0.0D

Var=Complex(0.0,0.0)Var=Dcomplex(0.0D,0.

0D) Var=’’或Var=”” Var=Ptr_New() Var=Obj_New()

数据类型函数 thisVar=Byte(variable) thisVar=Fix(variable) thisVar=Long(variable) thisVar=Long64(variable) thisVar=UInt(variable) thisVar=ULong(variable) thisVar=Ulong64(variable) thisVar=Float(variable) thisVar=Double(variable) thisVar=Complex(variable) thisVar=DComplex(variable) thisVar=String(variable)

None None

表1:IDL中的14种基本数据类型。表中显示了每种数据类型的字节数,创建变量的方法,

用语数据类型之间强制转换的IDL函数

正如所看到的,IDL是一个善于处理矢量或数组数据的软件,所以有大量的IDL命令用于创建不同数据类型的矢量和数组。特别是,有许多创建各类数据类型的数组的函数,该数组的每个元素的初始值为零,而且还有许多创建各类数据类型的数组的函数,该数组的每个元素的初始值为其在数组中的索引位置。在表2中将看到这些函数列表。例如,创建100*100初始值为零的字节型数组,输入:

扩展:idl / idl教程 / idl程序设计

IDL>array=BytArr(100,100)

18

IDL IDL入门教程

创建一个有100个元素的浮点型矢量,初始数值为从0到99,输入:

IDL>vector=FIndGen(100)

将在本书中看到使用这些IDL函数的各种方式。[www.loach.net.cn)

动态改变变量的属性

IDL最强大的功能之一是大多数命令都能在任何数据类型或组织结构上起作用。这是因为IDL在运行时能改变变量的数据类型和组织结构(像世界上其它强大的事物一样,这种动态改变变量的属性的能力也有潜在的巨大危险!必须小心,确信知道正在使用哪种数据)。例如,在IDL中,本质上讲变量是毫无意义的(像在Fortran或者C程序中),因为这种变量的数据类型很容易改变。例如:

num=3 ; Initialize NUM as a scalar integer.

num=num*5.2 ; Variable NUM changes to a float!

变量num被初始化为一个整数,由于数学运算的结果和重新赋值,它被动态地改变成浮点数值。这是因为IDL在数学计算当中为了保证最高的精度,将低精度的数据类型提升为高精度的数据类型。当num被再赋值(在等号的左边),它被提升为一个浮点数去保持等号右边计算的精度。思考下面这个例子:

result=4*x

在这种情形下,是不可能知道变量会产生哪种数据类型和组织结构,因为对x变量一无所知。事实上,结果主要取决于变量x的数据类型和数据结构。如果x是10个元素的浮点矢量,结果将会是10个元素的浮点矢量。如果它是100*200的长整数数组,结果也将是100*200的长整数数组。注意如果x有一个字节的数据类型,那结果将是一个整数数据类型(在这种情形下,组织结构并没有多大影响)。这是由于被整数乘的结果。

记住等号右边的表达式总是在将数据类型和组织结构赋予等号左边的变量前计算的。IDL将变量提升到能保持表达式的计算精度的数据类型。

注意整型变量

关于整型变量想简单地提一提,以免使用它们时遇到麻烦。有两种常见的错误。第一种涉及到整数数学。思考一下这个示例:

result=12/5

也许期望的是一个值为2.4浮点变量,但是它不是的,而是一个值为2的整数。知道为什么吗?是的,方程式右边的两个数字为整数。这是一个整数除法的例子。如此之下,找出错误并不难,但有时问题会更微小。

例如,假如想知道IDL图形窗口的比率。窗口的大小(像素点或整数值)被储存在两个系统变量中。也许会写出如下的IDL代码:

aspect=!D.X_Size / !D.Y_Size

它可以花掉很长的时间找出为什么比率为零。正确的方法是写出代码以强制将一个整数值变成为一个浮点,如下:

aspect=Float(!D.X_Size) / !D.Y_Size

现在的比例变量就是一个所期望的浮点数了。 数据类型

字节型 初始化函数 BytArr 产生索引值的函数 BIndGen

19

IDL IDL入门教程

16位有符号整型 32位有符号长整型 64位有符号整型 16位无符号整型 32位无符号长整型 64位无符号整型

浮点型 双精度浮点型

复数 双精度复数 字符串 指针 对象

IntArr LonArr Lon64Arr UIntArr ULonArr ULon64Arr FltArr DblArr ComplexArr DComplexArr StrArr PtrArr ObjArr

IndGen LIndGen L64IndGen UIndGen ULIndGen UL64IndGen FIndGen DIndGen CIndGen DCIndGen SIndGen None None

表2: IDL函数可以创建矢量和多维数组,并将其每个元素初始为0或为它们本身的索引号码。[www.loach.net.cn]

使用整型变量另外一个常遇到的问题是没有意识到IDL的整型在其它编程语言中被称为短整型。或者说,IDL的一个整型只有两个字节长。整型在其它程序语言中有四个字节(四个字节的整数在IDL的整数中是一个长整数)。

两个字节的整数只能大到32767。大于这个值通常由于“溢出”而被IDL当作为负数。用短整数会在两种情况下遇到麻烦。首先,在循环中没有考虑到短整数的因素,例如,假如想读一个数据文件,但不知道有多少行。可以写入如下代码: COUNT=0

WHILE NOT EOF(lun) DO BEGIN READF,lun,temp data(count)=temp COUNT=count+1 ENDWHILE

如果数据文件多于32,768行,这个代码就失败了。原因是count变量初始为一个整数,这个代码更好的写法如下: count=0L

WHILE NOT EOF(lun) DO BEGIN READF,lun,temp data(count)=temp count=count +1L ENDWHILE

现在随便读取多少行都可以。

另外一个常犯这种错误的地方是For循环中。按如下写法来写出For循环命令是一个好注意: FOR j=OL,num-1 DO...

第二种在使用短整型可能会遇到麻烦的方式是,当在读取用其它编程语言生成的数据时(或者反过来)。如果读取用C或Fortran程序生成的整型数据,应该确保在IDL中用长整型来读这些数据。同样,应该用长整型数据来写那些将被C或Fortran程序视为整型来读入的文件。

20

IDL IDL入门教程

使用矢量和数组

IDL是一种在善于处理矢量和数组中的程序语言(IDL的第一个版本的原形是APL,是一种在数组运算上非常优秀的程序语言)。(www.loach.net.cn]要成为一个高效的IDL程序员,必须知道怎样对数组进行数学运算。在本书中,将看到许多这方面的例子,但在开始前,需要注意两个重点。

创建矢量

在IDL命令行,可以用一对方括号创建一个矢量(矢量只是一维的数组)或一个数组,如下: IDL>vector=[1,2,3]

这是一个整型矢量,因为数据值为整型值。

可以用Help命令,获取关于数据类型和变量组织结构的信息,如下:

IDL>Help,vector

VECTOR INT =Array[3]

如果想增加从第四个元素到矢量中,在IDL中可以很轻松地完成。只需键入: IDL>vector=[vector,4]

IDL>Print,vector

1 2 3 4

数组下标的应用

假设打算在数组的第二和第三个元素之间添加另外一个元素,数组下标可以帮助完成。数组下标的上界和下界被冒号隔开。例如,指定上述矢量的前三个元素,如下所示: IDL>Print, vector(0:2)

1 2 3

注意,矢量下标的起始值是0,而不是1,并且矢量下标使用圆括弧以示区别。这使得有时很难将一个函数调用和一个数组下标引用区别开来。为了解决这个问题,IDL允许使用方括弧来引用数组下标。也就是说,当运行IDL5时,可以键入:

扩展:idl / idl教程 / idl程序设计

IDL>Print, vector[0:2]

本书已被修改成使用方括弧引用下标,以避免同函数调用相混淆。倘若正在使用IDL的IDL4.x版本,要运行此命令就得用圆括弧代替方括弧。

要用数组下标将另一个元素插入第二和第三个元素之间,可键入:

IDL>vector=[vector[0:1],5,vector[2:3]]

IDL>Print, vector

1 2 5 3 4

矢量也可用上表中谈到的数组创建函数建立。例如,建立一个值在0到50之间的6个元素浮点矢量,可键入:

IDL>vector=FIndGen(6)*10

IDL>Print,vector

0.000000 10.0000 20.0000 30.0000 40.0000 50.0000

21

IDL IDL入门教程

数组的建立

数组也可以在IDL命令行中建立。(www.loach.net.cn]例如,可以建立一个两行三列的数组,如下所示: IDL>array=[[1,2,3],[4,5,6]]

IDL>Print, array

输出IDL输出窗口中将会如下所示:

1 2 3

4 5 6

注意,这等同于先建立一个矢量,然后Reform命令将此变形为一个三行二列的数组,如下所示:

IDL>vector=IndGen(6)+1

IDL>array=Reform(vector,3,2)

IDL>Print, array

这表明矢量和排列是以行的顺序存储在IDL中的。这一点在编写IDL程序的过程中非常重要,因为将经常用到IDL这种数据存储方式的优势。

数组中元素的存取

假设想读出刚建立的数组中位于第一列第二行的元素(元素的值为4),可以键入: IDL>Print,array[0,1]

注意,下标的顺序先是列标,后是行标。这正好与已习惯的线性代数中的矩阵或行列式相反(同时,行标与列标比想象的小1,因为排列下标值的起始值是0而不是1)。

列-行下标源于极大的图像数据,IDL最初就是为处理这种数据而开发的。数据中的一行对应图像的一个独立扫描行。这种数据存储形式使数据操作迅速而精确。决定一套软件是使用列-行下标,还是使用行-列下标,完全可以自由决定。没有任何特殊原因选此弃彼。

可以使用一维下标来存取该数组中的同一个元素。要知道数组元素是以行顺序存储的,所以获得数组中的第四个元素。可以键入以下语句来存取:

IDL>Print, array[3]

用一维下标存取多维数组,这在许多IDL程序中是一个强大的工具。

也可以用一维向量来做数组的下标。例如,倘若要存取数组中的第一,二,四和第六个元素,可键入:

IDL>indices=[0,1,3,5]

IDL>Print,array[indices]

矢量和子数组的提取

IDL可很容易地从数组内提取出矢量和子数组。例如:看看这个拥有随机数据的数组: IDL>data = RandomU(seed, 10, 20)

想提取出第6-10列和第12-15行的数据,可键入:

IDL>subarray = data[5:9, 11:14]

如果要将第8列的数据画出来,可以使用下标*代表所有的行,如下所示:

IDL>Plot, data[7,*]

22

IDL IDL入门教程

要建立一个第14行的矢量,键入:

IDL>vector = data[*,13]

要建立一个数据为数组中最后5行的数组,键入:

IDL>subarray = data[*,15:19]

IDL>Help, subarray

现在可以看到子数组是一个10列*5行的数组。(www.loach.net.cn]

同样可以用*代表剩下的所有数据。例如,用数组的最后5列建立一个子数组,也可键入: IDL>subarray = data[5:*,*]

IDL>Help, subarray

通过对本书中范例的练习,会对数组以及数组的处理方法了解得更多。

使用IDL图形窗口

通过对本书中范例的练习,会对IDL图形窗口的了解得更多,但在开始之前,最好先了解下面一些东西。

图形窗口的建立

首先,一个图形窗口可直接用Window命令建立,或是在没有窗口打开的情况下,间接通过运行图形显示命令来打开。例如,可以建立并启动一个窗口,只须键入:

IDL>Window

注意,此窗口的标题栏中有一个0,这是此窗口的索引号。当图形窗口建立后,每个图形窗口都有唯一的一个图形窗口索引号。Window命令如果没有任何定位参数总是创建出索引号为0的图形窗口。称之为“窗口0”。在IDL的一次运行中,最少可同时打开128个图形窗口。可以为0到31号图形窗口指定一个索引号。对于32到127号图形窗口,可以用Window命令带上Free关键字(以下将谈到)来创建,IDL将为它们赋上索引号。例如:想创建一个索引号为10的图形窗口,键入:

IDL>Window, 10

倘若某个索引号图形窗口的窗口已经存在,再用Window命令创建相同索引号图形窗口,Window命令将首先删除旧窗口,然后建立一个带有此索引号的新窗口。

如果愿意(当在IDL程序中建立窗口时,这通常是一个不错的注意),可以用一个未用的索引号或者已经打开但是空白窗口的索引号来创建新的图形窗口。关键字Free即为此目的而设,如下所示:

IDL>Window, /Free

用关键字Free建立的图形窗口,将会具有一个大于31的索引号。关键字Free是建立索引号大于31的常规图形窗口的唯一途径。

确定当前图形窗口

现在在显示器上至少已经打开了三个图形窗口,但只有一个是当前图形窗口。当前图形窗口用于接受图形命令的输出结果。当前图形窗口的索引号总是存储在!D.Window系统变量中。如果没有创建和打开图形窗口,系统变量!D.Window的值为-1。

23

IDL IDL入门教程

可以创建一个图形窗口,并存储其图形窗口索引号,以便以后删除该窗口或使其成为活动窗口。(www.loach.net.cn)可键入:

IDL>Window, /Free

IDL> thisWindowIndex = !D.Window

使图形窗口成为当前窗口

为使一个窗口成为当前图形窗口(可在其内显示图形),可使用Wset命令和图形窗口索引号来设定。例如,希望当前图形窗口为10号窗口时,键入:

IDL>Wset, 10

随后所有的图形命令的结果都将显示到10号窗口内。

注意,当一个图形窗口创建完成后,该窗口即成为当前窗口(但是,用Widget_Draw产生的窗口不是这样)。为了在某个窗口内绘制图形,该窗口必须是当前图形窗口。

扩展:idl / idl教程 / idl程序设计

删除图形窗口

可用Wdelete命令和图形窗口的索引号删除图形窗口。被删除的图形窗口不必是当前图形窗口。例如,删除窗口10,键入:

IDL>Wdelete, 10

删除当前显示器上的所有图形窗口有一个技巧:

IDL>WHILE !D.Window NE –1 DO Wdelete, !D.Window

图形窗口的位置和尺寸

在创建图形窗口时,图形窗口的位置和尺寸是根据内部运算规则确定的。在Window命令中,用关键字可以设置图形窗口的位置和尺寸。例如,用关键字XSize 和YSize创建一个宽200像素,高300像素的窗口,键入:

IDL>Window, 1, XSize=200, YSize=300

可用相对于显示器左上角的像素坐标或设备坐标来定位窗口。例如,用关键字XPos 和YPos将窗口的左上角定位于显示器(75,150)处,键入:

IDL>Window, 2, XPos=75, YPos=150

将图形窗口设置到显示器最前面

创建一个图形窗口时,该窗口拥有输入焦点,同时也成为当前图形窗口。也就是说,对于窗口管理器来讲,该图形窗口现在为激活窗口(仅仅因为一个图形窗口拥有窗口输入焦点,并不意味它是当前图形窗口)。为了输入一个命令,不得不将窗口焦点移回到命令输入窗口。在某些平台上,特别是在PC机上,这会导致图形窗口隐藏到其它窗口后面。

有时,在显示器上一个图形窗口隐藏其它窗口的后面,想将该窗口拖到前面以便能看见。在不改变窗口输入焦点的情况下,要将一个图形窗口显示在前面,可用Wshow命令和图形窗口索引 24

IDL IDL入门教程

号来完成。(www.loach.net.cn)

IDL>Wshow, 1

注意,光标和窗口焦点仍在键入IDL命令的命令输入窗口或其它窗口内。

用Wshow命令将窗口显示在前面但并不将窗口改变为当前窗口。如果既想将该窗口拖到前面,又想将其变为当前窗口,那么可同时键入Wshow 和Wset命令:

IDL>Wshow, 2

IDL>Wset, 2

注意,如果输入不带参数的Wshow命令,在显示器上将当前窗口拖到前面。当不清楚哪个是当前图形窗口和只想将当前窗口拖到前面而不从IDL命令窗口移动开焦点时,这个命令是非常有用的。

IDL>Wshow

注意,在PC机和Macintosh机器上,可以用ALT-TAB键或者OPTION-TAB键来循环选择已经在显示器上打开的窗口,让其可见并拥有窗口焦点。

在图形窗口上设置标题

有时希望在图形窗口上设置标题,而不仅仅是图形窗口索引号。可以使用Title关键字将标题设置到窗口上,键入:

IDL>Window, Title=’Example IDL Graphics Commands’

清除图形窗口内容

可以使用Erase命令清除当前图形窗口内容:

IDL>Erase

如果想用一种特定的颜色索引号,去清除当前图形显示(如果在24位颜色模式下可以用一个24位颜色值),可以用color关键字。例如,可以用以下命令实现用炭灰色清除当前图形显示: IDL>TVLCT, 70, 70, 100

IDL>Erase, Color=100

想清除非当前图形窗口(系统变量!D.Window指向的窗口)的内容,必须使该窗口成为当前图形窗口,接着使用Erase命令。

25

IDL IDL入门教程

第二章 简单的图形显示

本章概述

科学分析最基本的能力就是以简单的线画图、等值线图和曲面图来显示所研究的数据。(www.loach.net.cn]在这一章中,将知道用这些方式来显示数据是多么容易。也将学会用系统变量和关键字来定位和标注简单的图形显示。

将学会如下几点:

1. 如何用Plot命令将数据显示为线画图。

2. 如何用Surface和Shade_Surf命令将数据显示为曲面图。

3. 如何用Contour命令将数据显示为等值线图。

4. 如何在显示窗口上定位显示图形。

5. 如何用公共关键字来标注和自定义图形显示。

IDL中简单的图形显示

IDL中一个简单的图形显示可认为是栅格图形的一个实例。也就是说,可用Plot, Contour或者Surface命令通过一种算法来点亮显示窗口内相应的像素点而形成栅格图形。这种栅格图形没有永久性。换言之,一旦IDL显示图形和点亮相应的像素点后,IDL就不知道自己做了些什么。这意味着,在用户重置图形窗口大小时,IDL无法进行相应的响应。总之,在这种模式下图形显示不能被刷新,除非再次输入图形命令。

但是,栅格图形命令在IDL中被广泛应用,因为它们简单快捷。而且,将看到,如果仔细地用栅格图形命令编写IDL程序时,可以克服许多与栅格图形命令相关的限制。本章将介绍一些关于如何用栅格图形命令写出可调节尺寸的IDL图形窗口或进行直接硬拷贝输出的必备概念。本章的图形命令都是Research Systems公司所说的直接图形。

另外一种被Research Systems公司称为对象图形的图形方式在IDL5.0中被引入。对象图形使用时相对难一点,但它在IDL编程方面更强大更灵活。对象图形不是为了在命令行使用而开发的,而是用在IDL的程序中,特别是用于带有界面的程序中(带有图形用户界面的程序)。本书对对象图形不做介绍。

创建线画图

生成线画图最简单的方法是绘出一个矢量。可以用LoadData命令打开时序数据集。LoadData命令是本书所带的一个IDL程序(详细细节参考第5页的“本书中所用的IDL程序和数据文件”)。它用来装载本书的编程例子中所需的数据。键入如下语句以查看所能使用的数据集:

IDL>curve=LoadData()

如果输入LoadData命令时忘掉了括号,需要在它正常工作前重新编译LoadData程序。原因是,IDL在命令行会认为它是一个变量并进行相应地处理。重新编译后,“loaddata”这个函数名在IDL的函数名列表中。键入:

26

IDL IDL入门教程

IDL>.Compile LoadData

时序数据是在LoadData数据列表上的第一个数据集。(www.loach.net.cn]点击它,数据就被装入到curve变量中。另外一种选择第一个数据集的方法是,按如下方法使用LoadData:

IDL>curve=LoadData(1)

要查看curve变量如何被定义,键入:

IDL>Help, curve

CURVE FLOAT =Array[101]

将发现curve是一个具有101个元素的浮点矢量(或一维数组)。

要绘出该矢量,可键入:

扩展:idl / idl教程 / idl程序设计

IDL>Plot, curve

IDL试图用少量的信息尽可能地绘出漂亮的线画图。在这种情况下,x轴或水平轴被标识为从0到100,这与矢量中的元素个数相对应。而y轴或垂直轴则是用数据坐标来标识(它是取决于数据的坐标轴)。

但大多数情况下,线画图用于显示一组数据(独立数据)相对另外一组数据(非独立数据)的关系。例如,上面的曲线可能代表在某段时间内采集数据的信号。可能需要绘制某个时刻的信号值。在这种情况下,需要一条与该曲线矢量具有相同元素个数的矢量(这样可以获得一一对应的相关性),并将该矢量转换为实验中所用的时间单位。例如,可以创建一个时间矢量,并绘出它与上述曲线矢量的关系图:

IDL>time=FIndGen(101)*(6.0/100)

IDL>Plot, time, curve

FIndGen命令创建一个元素值为0到100的共101

个元素的矢量。乘法因子按比例缩

图1:独立数据(时间)与非独立数据(曲线)关系图。

小每个元素的大小,最后的结果是一个元素值为0到6之间的共101个元素的矢量。图形输出结果应与图1相似。

注意,在此图中的坐标轴上没有相应的标题。在图上设置标题是很容易的,只要用XTitle和YTitle关键字既可实现。例如,为此曲线图加标题,可键入:

27

IDL IDL入门教程

IDL>Plot, time, curve, XTitle='Time Axis', $

YTitle='Signal Strength'

甚至可以用Title关键字对整个图形设置标题,键入:

IDL>Plot,time,curve,XTitle='Time Axis', $

YTitle='Signal Strengh',Title='Experiment 35M'

输出结果应与图2一样。[www.loach.net.cn]

图2:简单的带坐标轴标题和图形标题的线画图

注意图形显示应该为在黑色背景下的白线图,而上图显示为在白色背景下的黑线。这些插图包含在用IDL生成的PostScript文件中。一般情况下Postscript文件把图形颜色和背景颜色反过来。(参考189页的“问题:PostScript设备对背景颜色和图形颜色的不同处理”。)

注意,图形标题稍微大于坐标轴的标题。事实上,是1.25倍的关系。可以用CharSize关键字改变所有图形注记的大小。例如,可以将坐标轴标题的字符放大50%:

IDL>Plot, time, curve, XTitle='Time Axis', $

YTitle='Signal Strength', Title='Experiment 35M', $

CharSize=1.5

如果希望所有的图形显示的字符比正常情况下大,可以通过绘图系统变量上设置CharSize的大小,如下:

IDL>!P.CharSize=1.5

现在,所有后续的图形显示都将用较大的字符,除非用CharSize关键字在图形输出命令中特别地控制。

甚至可以用[XYZ]CharSize关键字单独改变每个轴的标识字符的大小。例如,如果想使Y轴的注记比X轴的大两倍,则可键入:

IDL>Plot, time, curve, XTitle='Time Axis', XCharSize=1.0,

$

28

IDL IDL入门教程

YTitle='Signal Strength', YCharSize=2.0

记住,[XYZ]Charsize关键字使用当前字符的大小作为基础计算出各自的大小。(www.loach.net.cn)当前字符的大小一般储存在!P.CharSize系统变量中。这意味着,如果设置XCharSize关键字为2,当!P.CharSize系统变量也被设置为2时,字符将比平常大四倍。

定制线画图

上面是简单的线画图,除了数据本身外,没有多少其它信息。然而,有许多方法可用来定制和标注线画图。Plot函数可以被50多种不同的关键字修饰。下面的事情也许想做一做:

1. 改变线型或粗细。

2. 使用符号,符号之间可以有线条和没有线条存在。

3. 创建自己的绘图符号。

4. 给线图加入颜色提示重要特性。

5. 改变刻度标记的长度或刻度标记之间的间隔。

6. 使用对数来标度图形坐标轴。

7. 改变绘图范围来绘出感兴趣的数据段。

8. 删除坐标轴或改变绘图方式。

改变线条的线型和粗细

例如,想用不同的线型画出数据。如画一条线型为长虚线的线条,可以这样实现: IDL>Plot, time, curve, LineStyle=5

对于线画图来说,可通过LineStyle关键字选用表3中列出的索引号确定不同的线型。例如,想使用虚线画出曲线,可以把LineStyle关键字的值设置为2:

IDL>Plot, time, curve, LineStyle=2 索引号

1

2

3

4

5 线型 实线 点线 虚线 划点线 划点点线 长虚线

表3:可以通过赋予LineStyle这个关键字不同索引号来改变线型

线画图中线的粗细同样能够被改变。例如,如果想使用比正常值粗3倍的虚线来显示图形,可键入:

IDL>Plot, time, LineStyle=2, Thick=3

用符号代替线条显示数据

假如想用符号代替线条显示数据,就象LineStyle关键字一样,也存在类似的索引号供选择,以确定不同的线画图符号。表4给出了能通过PSym(绘图符号)关键字来选择的索引号。例如, 29

IDL IDL入门教程

可以通过设置PSym为2,用星号来绘图,如下: IDL>plot, time, curve, Psym=2 输出的图形应与图3中的图形相似。[www.loach.net.cn]

图3:用符号而不是线条来显示线画图。

索引号 0 1 2 3 4 5 6 7 8 9 10 -PSym

绘图符号

无符号,通过线条连接点

加号 星号 点 菱形 三角形 方形 X

用户自定义符号(用UserSym过程来定义)

未用 直方图

负值表示用线条连接相应的符号

表4:这些符号索引号可以通过PSym关键字来引用以便在绘图中使用不同的符号。注意绘图符号

为负值时表示用线条来连接相应的符号。

30

IDL IDL入门教程

用线条和符号来显示数据

扩展:idl / idl教程 / idl程序设计

赋予PSym关键字一个负值就可以用线条将图形符号连接起来。[www.loach.net.cn]例如,可用实线与三角形符号绘出数据,键入:

IDL>Plot, time, curve, PSym=-5

为创建一个更大的符号,可用SymSize关键字。下面的语句画出的符号为正常的两倍。符号值为4时符号的大小为正常值的4倍,依此类推。

IDL>Plot, time, curve, PSym=-5, SymSize=2.0

创建自己的图形符号

如果富有创造力,甚至可以创建自己的图形符号。UserSym命令就用于此目的。在创建了一个特殊的图形符号之后,可通过设置PSym关键字为8来选择它。以下是一个创建五角星符号的例子。 x,y矢量定义五角星的顶点,它们的值为偏离原点(0,0)的位置。可以用UserSym命令通过设置关键字Fill创建一个填充的图形符号:

IDL>x=[0.0, 0.5, -0.8, 0.8, -0.5, 0.0]

IDL>Y=[1.0, -0.8, 0.3, 0.3, -0.8, 1.0]

IDL>TvLCT, 255, 255, 0, 150

IDL>UserSym, x, y, Color=150, /Fill

IDL>Plot, time, curve, PSym=-8, SymSize=2.0

输出结果应与图4相似。

图4:用UserSym程序创建的符号来绘制的图。

31

IDL IDL入门教程

用不同的颜色绘制线画图

可以用不同的颜色绘制线画图(颜色将在第83页的“IDL的颜色运用”中详细讨论。[www.loach.net.cn]现在,只须按如下键入TvLCT命令即可,以后将学到这个命令意味着什么。实质上,装载了三个颜色矢量,每个矢量的三个分量分别代表颜色的三个组成部分红,绿,蓝。这三种颜色矢量为碳灰,黄,绿色。)例如将颜色索引号1、2和3分别设置为碳灰,黄,绿色,键入:

IDL>TvLCT, [70,255,0], [70,255,255], [70,0,0], 1

在碳灰背景下绘黄色图,键入:

IDL>Plot, time, curve, Color=2, Background=1

如果只是想使线条成为不同的颜色,首先必须将NoData关键字打开来绘图,然后用OPlot命令(下面要讨论的)覆盖该图。例如,在碳灰色背景上绘制黄色外框,数据用绿色显示,键入: IDL>Plot, time, curve, Color=2, Background=1, /NoData

IDL>OPlot, time, curve, Color=3

限定线画图的范围

并非所有的数据都必须在一个线画图中绘出,可以用关键字限定绘图的数据量。例如,可仅绘出位于X轴上2至4之间的数据,键入:

IDL>Plot, time, curve, XRange=[2, 4]

或者仅绘出Y值在10至20之间,X值在2至4之间的部分数据图形,键入: IDL>Plot, time, curve, YRange=[10, 20], XRange=[2, 4]

也可以通过给定关键字数据范围来反转数据的方向。例如,可将Y轴的0点设置为图形的顶端,如下:

IDL>Plot, time, curve, YRange=[30, 0]

输出结果应与图5相似。

如果所选择的轴的范围不适合IDL关于坐标轴美观标记的规定,IDL将忽略所要求的范围。试一试如下的命令:

IDL>Plot, time, curve, XRange=[2.45, 5.64]

X轴上显示的范围将是从2至6,这并不是对IDL所要求的精度。为确保轴上显示的范围正如所要求的那样,可将XStyle关键字设置为1,如下:

IDL>Plot, time, curve, XRange=[2.45, 5.64], XStyle=1

下一节将学到更多关于[XYZ]Style关键字的知识。

32

IDL IDL入门教程

图5: 将Y轴0点设置为图形顶端的图形

改变线画图的风格

可以方便地改变线画图的许多特性,包括它们的外观形式。[www.loach.net.cn)例如,可能不在意线画图的方框。如果是这样,可以用[XYZ]Style这些关键字改变线画图的特性。表5给出了可通过这些关键字来改变线画图风格的值。例如,为除去方框线,只留下X轴或Y轴,可键入:

IDL>Plot, time, curve, XStyle=8, YStyle=8 值

1

2

4

8

16 对坐标轴的影响 精确的坐标轴范围 扩展坐标轴范围 不显示整个坐标轴 不显示外框(只画坐标轴) 屏蔽Y轴起始值为0的设置(只有Y轴有此属性)

表5:[XYZ]Style关键字参数表,用于设置坐标轴的属性。注意:这些值可以累加从而设置坐标

轴的多个而非单个属性。

可以完全隐藏一个轴。例如,仅用Y轴显示图形,可键入:

IDL>Plot, time, curve, XStyle=4, YStyle=8

33

IDL IDL入门教程

输出结果应与图6相似:

图6: 关闭X轴和方框只剩Y轴的线画图

可以用Y轴和Y方向的网格线来显示同一幅图:

IDL>Plot, time, curve, XStyle=4, YTickLen=1, YGridStyle=1

[XYZ]Style关键字可以一次设置坐标轴的多个特性。[www.loach.net.cn)可以通过累加适当的值来实现。例如,可以从表5中看出,强制使用精确的坐标轴范围的参数值为1,而用来删除方框线的参数值为8。为实现上述两项功能,即让X轴显示精确的范围又隐藏方框线,可将两个参数值相加: IDL>plot, time, curve, xstyle=8+1, xrange=[2, 5]

在线画图上创建网格线,通常可用TickLen关键字来完成。如下:

IDL>Plot, time, curve, TickLen=1

将[XYZ]TickLen关键字设置为一个负值可以创建向外的刻度标记。例如,为创建向外的刻度标记,可键入:

IDL>Plot, time, curve, TickLen=-0.03

在某个轴上创建向外的刻度标记,可将[XYZ]TickLen关键字设置为一个负值。例如,只在X轴上创建向外的刻度标记,键入:

IDL>Plot, time, curve, XTickLen=-0.03

可以用[XYZ]Ticks和[XYZ]Minor关键字,在一个轴上选择主要的和次要的刻度标记的个数。例如,在X轴上创建两个主要的刻度间隔,每个主要的刻度间隔内设置10个次要的刻度标记,键入:

IDL>Plot, time, curve, XTicks=2, XMinor=10, XStyle=1

在线画图上绘出多种数据集

没有必要限制自己仅仅用一组数据绘制线画图。IDL

扩展:idl / idl教程 / idl程序设计

程序允许在同一套坐标轴内显示任意多 34

IDL IDL入门教程

套数据。(www.loach.net.cn)OPlot命令就用于此目的。键入以下命令,输出结果应与图7相似:

IDL>Plot, curve

IDL>OPlot, curve/2.0, LineStyle=1

IDL>OPlot, curve/5.0, LineStyle=2

初始的Plot命令为以后的绘图建立数据比例(!X.S和!Y.S是比例参数)。或者说, !X.S和!Y.S系统变量告诉IDL如何在数据范围内取点以及如何将该点显示在设备坐标空间上。要确保初始图形有足够的轴长,以便包容以后绘制的所有图形,否则数据将被裁剪掉。可在第一个Plot命令中用XRange和YRange关键字来创建一个足够大的数据范围。为区别不同的数据集,可用不同的线型,不同的颜色,不同的图形符号等。Oplot命令接受很多被Plot命令接受的关键字。 IDL>TvLCT, [255, 255, 0], [0, 255, 255], [0, 0, 0], 1

IDL>Plot, curve, /NoData

IDL>OPlot, curve, Color=1

IDL>OPlot, curve/2.0, Color=2

IDL>OPlot, curve/5.0, Color=3

图7:在同一个线画图上可以绘制无限多套数据集

35

IDL IDL入门教程

图8:具有两个Y轴的线画图。[www.loach.net.cn]第二轴是用Axis命令来定位的。一定要用Save关键字来将数据

比例保存起来

在多个轴的图上显示数据

有时,希望在同一个线画图上显示两个或多个数据集,并用不同的y轴表示不同的数据集。使用Axis命令很容易建立所需数量的坐标轴。使用Axis命令的关键是使用save关键字来存储正确的绘图比例参数(即存储在!X.S和!Y.S系统变量中的比例参数),以便后续图形的调用。

下面的例子在已绘出一幅图后,用带Save关键字的Axis命令建立第二个Y轴。OPlot命令中的曲线将调用通过Axis命令保存的比例因子,以确定其在图形中的位置。正确的命令是如下: IDL>Plot, curve, YStyle=8, YTitle='Solid Line', $

Position=[0.15, 0.15, 0.85, 0.95]

IDL>Axis, YAxis=1, YRange=[0, Max(curve*5+1)], /Save, $

YTitle='Dashed Line'

IDL>OPlot, curve*5, LineStyle=2

Position关键字用来确定第一个图形在页面内的位置。为了解更多关于Position关键字的知识,可参阅第48页的“在图形显示窗口中确定图形输出位置”章节。输出图形应与图8相似。 创建曲面图

在IDL程序中,任何二维的数据组都可以用Surface命令生成一个曲面图(经过自动消隐)。首先,必须打开数据文件,用LoadData命令打开 Elevation Data

数据集。键入:

36

IDL IDL入门教程

IDL>peak=LoadData(2)

通过键入Help命令,可以发现这是一个41*41的浮点数组。(www.loach.net.cn)键入:

IDL>Help, peak

这个数组可以用一个命令使之视面图:

IDL>Surface, peak, CharSize=1.5

输出结果应与图9相似。

图9:利用高程数据生成简单的曲面图。

注意,如果仅用单个数组作为变量调用Surface命令,它将把该数组作为其元素个数(此例在X和Y方向都为41)的函数来绘图。(可以使用CharSize关键字来改变字符的大小,以便更容易看清楚)。但是,正如前面使用Plot命令一样,可以规定X和Y轴的数值,以便显示的图形具有实际意义。例如,X和Y轴的数值可以是经纬度坐标。这里,使纬度范围为从24度到48度,经度范围为-122度到-72度:

IDL>lat=FIndGen(41)*(24./40)+24

IDL>LON=FIndGen(41)*50.0/41-122

IDL>Surface, peak, lon, lat, XTitle='Longitude', $

YTitle='Latitude', ZTitle='Elevation', CharSize=1.5

输出结果应与图10相似。

37

IDL IDL入门教程

图10:一个具有实际意义坐标值的曲面图。(www.loach.net.cn]

以上命令中的lon和lat参数是单调递增并且是规则的。它们描述了曲面网格线的位置。但网格没有必要是规则的。试想一下,如果使经度数据点不规则分布会出现什么情况。例如,可以键入以下命令模拟随机分布的经度点:

IDL>seed=-1L

IDL>newlon=RandomU(seed, 41)*41

IDL>newlon=newlon[Sort(newlon)]*(24./40)+24

IDL>Surface, peak, newlon, lat, XTitle='Longitude', $

YTitle='Latitude', ZTitle='Elevation', CharSize=1.5

现在发现经度X值是没有规则分布的。尽管看起来数据被重新取样了,然而却不是。能很容易地在经度和纬度数据点指定的位置处画出曲面图的网格线。输出结果应与图11相似。

38

IDL IDL入门教程

图11:同样的曲面图,但其X矢量具有不规则的空间分布

定制曲面图

有70多个不同的关键字可以用来修饰曲面图。(www.loach.net.cn]实际上,许多关键字在Plot命令中已经学过。例如在上面的代码中,就使用了相同的标题关键字对曲面图的轴进行标记。然而要注意,当用Title关键字时,所添加的标题被旋转了,从而保证标题总是位于曲面图的XY平面内。键入: IDL>surface, peak, lon, lat, XTitle='Longitude', $

YTitle='Latitude', Title='Mt.Elbert', Charsize=1.5

这并非总是所希望的。如果想使图形标题位于与显示面平行的平面内,就必须用Surface命令绘制曲面图,而用XYOutS命令显示标题(第55页有关于XYOutS命令的详细信息)。比如,键入:

IDL>Surface, peak, lon, lat, Xtitle=’Longitude’, $

Ytitle=’Latitude’, Charsize=1.5

扩展:idl / idl教程 / idl程序设计

IDL> XYOutS, 0.5, 0.90, /Normal, Size=2.0, Align=0.5, $

‘Mt.Elbert’

旋转曲面图

在观察曲面图时可能希望能旋转一个角度。曲面图可以用Ax关键字使其绕X轴或用Az关键字使其绕X轴旋转。当从轴上的正值向原点观察时,曲面图以逆时针方向,按某个角度值旋转。当Az和Ax关键字被忽略时其缺省值是30度。例如,使曲面图绕Z轴旋转60度,绕X轴旋转35

度,则可键入:

39

IDL IDL入门教程

IDL> Surface, peak, lon, lat, Az=60, Ax=35, Charsize=1.5

输出结果应与图12相似。(www.loach.net.cn)

图12:用Az和Ax关键字使曲面图旋转

为曲面赋色

有时,可能想为曲面图上赋上颜色以强调某种特性。给曲面图着色是很简单的,只需使用在线画图中用过的赋色关键字即可。(颜色将在第83页的“IDL的颜色运用”中详细讨论。现在,只须按如下键入TvLCT命令即可,以后将学到这个命令意味着什么。实质上,装载了三个颜色矢量,每个矢量的三个分量分别代表颜色的三个组成部分红,绿,蓝。这三种颜色矢量为碳灰,黄,绿色)。例如,在碳灰色背景上创建一个黄色的曲面图,可键入:

IDL> TvLCT, [70, 255, 0], [70, 255, 255], [70, 0, 0], 1

IDL> Surface, peak, Color=2, Background=1

如果想使曲面图的底面的颜色不同于顶面,比如说绿色,可以使用Bottom关键字来实现: IDL>Surface, peak, Color=2, Background=1, Bottom=3

如果想将轴以不同的颜色显示,比如绿色,而不是曲面,必须键入两个命令。第一个命令使用NoData关键字,只将轴绘出。第二个命令是在关闭轴线后绘出曲面本身。查看第31页的表5,了解[XYZ]Style关键字的参数值及其含意:

IDL>Surface, peak, Color=3, /NoData

IDL>Surface, peak, /NoErase, Color=2, Bottom=1,XStyle=4, YStyle=4, ZStyle=4

用不同的颜色画出曲面的格网线也是有可能的,而不同的颜色代表不同的数据。比如,可用第二个数据集覆盖第一个,第二个数据集含有对第一个数据集的格网进行着色后的信息。

为了说明如何工作,可打开一个名为Snow Pack的数据集,并用以下这些命令将此数据作为 40

IDL IDL入门教程

一个曲面显示。(www.loach.net.cn]注意,Snow Pack数据集的大小与peak数据集一样,都是41*41浮点数组: IDL>snow=LoadData(3)

IDL>Help, snow

IDL>Surface, snow

现在,通过用snow的变量值对peak变量的格网着色,将snow变量中的数据覆盖到peak变量中数据的上面。首先,用LoadCT命令装载色彩表内的一些颜色。实际的阴影处理是通过shades关键字完成的,如下:

IDL>LoadCT, 5

IDL>Surface, peak, Shades=BytScl(snow, Top=!D.Table_Size-1)

注意,必须用BytScl命令将snow数据集调整为所使用IDL时的色彩数。如果调整失败,只能看到一套坐标轴,而看不到曲面显示。这是因为,数据必须调整到曲面阴影处理时所需的0到255的范围。

修改曲面图外观

有很多关键字可以用来修改曲面图的外观或形式。例如,可以显示一个带边缘的曲面图。使

用Skirt关键字来指定边缘该画到何处。试试下面命令:

IDL>Surface, peak, Skirt=0

IDL>Surface, peak, Skirt=500, Az=60

图13:带边缘的曲面图

上面第一个命令的输出结果应与图13相似。

如果仅绘出水平线,获得一种层叠线形图,比如,键入:

IDL>Surface, peak, /Horizontal

如果愿意,可以通过关键字来只显示曲面的底面或顶面,而不是两者都显示(缺省是两者都 41

IDL IDL入门教程

显示)。(www.loach.net.cn)键入:

IDL>Surface, peak, /Upper_Only

IDL>Surface, peak, /Lower_Only

有时可能只想显示曲面本身,而不需要轴线。 可键入:

IDL>Surface, peak, XStyle=4, YStyle=4, ZStyle=4

创建阴影曲面图

创建阴影曲面图同样很简单,可使用Gouraud光源阴影算法创建阴影曲面图,键入: IDL>Shade_Surf, peak

Shade_Surf命令接受大多数被Surface命令接受的关键字。例如,如果想旋转阴影曲面,可以键入:

IDL>Shade_Surf, peak, lon, lat, Az=45, Ax=30

输出图形应与图14相似。

图14: 用Gouraud光源阴影算法生成的阴影曲面图

改变阴影处理参数

用Set_Shading命令可以改变Shade_Surf命令所使用的阴影处理参数。例如,要将光源的光线的方向从平行Z轴的默认值[0,0,1]改变为平行X轴的方向[1,0,0],可键入:

IDL>Set_Shading, Light=[1, 0, 0]

IDL>Shade_Surf, peak

也可以从色彩表中挑选哪种颜色索引号用作阴影处理。例如,当想把红色色普表(色普表3) 42

IDL IDL入门教程

装载到颜色索引号100到199之中,并将之用于阴影处理,可键入:

IDL>LoadCT, 3, NColors=100, Bottom=100

IDL>Set_Shading, Values=[100, 199]

IDL>Shade_Surf, peak

注意将光源位置和颜色参数恢复原值,否则练习的继续,可能会造成混乱。(www.loach.net.cn]

IDL>LoadCT,5

IDL>Set_Shading, Light=[0,0,1], Value=[0,!D,Table_Size-1]

用其它数据集为阴影处理提供参数

首先,就象Surface命令一样,其它数据集也可以为阴影处理时的各数据点提供颜色值。正如前述,

扩展:idl / idl教程 / idl程序设计

可以用Shades关键字为曲面上各点指定颜色索引号。每个像素点的阴影处理都是根据该点周围数据值通过插值求出。例如,下面是一个用snow变量生成的阴影曲面图:

IDL>Shade_Surf, snow

现在用这个数据集来对最初的高程数据集进行阴影处理,键入:

IDL>Shade_Surf,peak,lon,lat, Shades=BytScl(snow, Top=!D.Table_Size)

输出结果应如图15所示:

图15:用snow数据集对peak数据进行阴影处理

如果要求根据数据点的高程值来对曲面进行阴影处理,可简单地对数据集本身进行字节比例缩放即可,键入:

IDL>Shade_Surf, peak, Shades=BytScl(peak, Top=!D.Table_Size)

将另一数据集覆盖在曲面图上是一种给数据升维的方法。例如,可将一组数据集覆盖在一个三维曲面图上,就可以直观的获得四维的信息。如果同时让两组数据集合随时间活动起来,就可以 43

IDL IDL入门教程

直观的获得五维信息。[www.loach.net.cn](关于数据动画参阅104页的“IDL的动画数据”)

有时只是想将原始曲面覆盖在经过阴影处理的曲面图上,通过结合使用Shade_Surf命令和Surace命令可轻松的做到。例如:

IDL>Shade_Surf, peak

IDL>Surface, peak, /NoErase

创建等值线图

在IDL中,任意二维数组都可以用一个Contour命令显示为等值线图。如果已经在这次IDL运行中定义了peak变量,可直接使用该变量。如果没有定义,可以使用LoadData命令来载入Elevation Data中的数据集。键入:

IDL>peak=LoadData(2)

IDL>Help, peak

这个数据集通过一个简单的命令即可显示为等值线图(图16):

IDL>Contour, peak, CharSize=1.5

图16:一个基本的等值线图,注意X、Y轴的标记代表该数组中的元素个数

注意,如果仅用单个二维数组作为参数调用Contour命令,它将把该数组作为其元素个数(此例在X和Y方向都为41)的函数来绘图。如前述所用Surface命令一样,可以指定X轴和Y轴的数值,以便使其具有实际意义。例如,可以象前述一样使用经度和纬度矢量。如下所示: IDL>lat=FIndGen(41)*(24./40)+24

IDL>lon=FindGen(41)*50.0/40-122

IDL>Contour, peak, lon, lat, XTitle='Longitude', $

YTitle='Latitude'

44

IDL IDL入门教程

注意轴被自动缩放了。(www.loach.net.cn)从很多地方可以看到这一点。首先,等值线没有延伸到等值线图的边缘,其次,可以发现轴上的标记与lon矢量和lat矢量的最小值和最大值不同。

IDL>Print, Min(lon), Max(lon)

IDL>Print, Min(lat), Max(lat)

为了防止轴的自动缩放,可以设置XStyle和YStyle关键字,如下:

IDL>Contour, peak, lon, lat, XTitle='Longitude', $

YTitle='Latitude', XStyle=1, YStyle=1

该命令得到图形应如图17所示。

图17:具有实际数量意义的等值线图

在早期的IDL版本中,Contour 命令使用所说的单元画法来计算并绘画数据的等值线。在这种方法中,等值线图是从图底画到图顶。这种方法是有效的,但是它不允许选项,比如标注等高线。而单元跟踪法被用来完整地画出围绕等值线图的每一条等值线。这需要较长的时间,但可以允许对等值线作更多的控制。例如,等值线可以断开用于等值线的标注。这种单元跟踪法可以用Follow关键字来调用:

IDL>Contour, peak, lon, lat, XStyle=1, YStyle=1,

/Follow

45

IDL IDL入门教程

从IDL5版本开始,Contour命令一般都使用单元跟踪法来绘制等值线图。(www.loach.net.cn)所以,Follow关键字已经过时了。但该关键字仍然被使用,是因为它对自动标注其它每条等值线的有益作用。选择等值线数目

缺省情况下,IDL选择6条匀称的等值线间隔(即有5条等值线)绘制等值线图。但是,可以用几种不同的方法改变缺省值。例如,

可以用Nlevels关键字告诉IDL需要绘制多少条等值线。IDL将计算出等间隔的等值线间隔数。例如,要绘制具有12条等间隔的等值线图,可键入: IDL>Contour, peak, lon, lat, Xstyle=1, Ystyle=1, /Follow, $

Nlevels=12

输出结果应与图18相似。可选择高达29条的等值线。

图18:这是将等值线级别设置为12的等值线图。注意,每隔一条等值线都会标注一条,这

是使用Fellow关键字的一种副作用。

不幸的是,尽管IDL文档称IDL将采用给定的等间隔的等值线间隔数,但事实上不是这样。如果注意观察所创建的等值线图,会注意到IDL只计算出少于12条的间隔线。显然,NLevels关键字的值在IDL中只能作为等值线选择算法中的一个“建议”。

因此,大多数IDL程序员都是自己计算等值线数目。例如,能精确规定哪条等值线应该画,并用Levels关键字传给Contour命令,而不是用NLevels关键字,如下所示:

IDL>vals=[200, 300, 600, 750, 800, 900, 1200, 1500]

IDL>Contour, peak, lon, lat, XStyle=1, YStyle=1, /Follow, $

46

IDL IDL入门教程

Levels=vals

要选择12个间距相等的等值线间隔,可编写如下代码:

IDL>nlevels=12

IDL>step=(Max(peak)-Min(peak))/nlevels

IDL>vals=Indgen(nlevels)*step+Min(peak)

IDL>Contour, peak, lon, lat, XStyle=1, YStyle=1, /Follow, $

Levels=vals

如果喜欢,可以C_Labels关键字精确的指定哪一根等值线应该标注。(www.loach.net.cn)这个关键字是一个其元素与等值线数目相等的矢量(如果元素个数与等值线数目不匹配,那么元素就不能象其它关键字那样循环使用)。如果某元素的值是1(或更精确,只要是正数),相应的等值线就给予标注;如果某元素的值是0,相应的等值线就不予标注。如果某条等值线没有元素值与之对应时,那么这条等值线就不标注。例如,要标注第一,第三,第六和第七条等值线,可键入:

扩展:idl / idl教程 / idl程序设计

IDL>Contour, peak, lon, lat, XStyle=1, YStyle=1, /Follow, $

Levels=vals, C_Labels=[1, 0, 1, 0, 0, 1, 1, 0]

要标注所有的等值线,可以使用Replicate命令来将1复制所需要的次数。键入:

IDL>Contour, peak, lon, lat, XStyle=1, YStyle=1, /Follow, $

Levels=vals, C_Labels=Replicate(1, nlevels)

修改等值线图

等值线图可用与Plot命令和Surface命令中相同的关键字进行修改。但是仍然还有许多仅适用于Contour命令的关键字。它们中的大部分经常用于修改等值线本身。例如,用坐标轴标题注释等值线图,可键入:

IDL> contour, peak, lon, lat, Xstyle=1, Ystyle=1, /Follow, Xtitle=’Longitude’, Ytitle=’Latitude’, $ Charsize=1.5, Title=’Study Area 13F89’, Nlevels=10

也可以用C_Annotation关键字在等值线上标注释。可以用字符串标记每一条等值线: IDL> contour, peak, Xstyle=1, Ystyle=1, /Follow, $

Xtitle=’Longitude’, Ytitle=’Latitude’, Charsize=1.5, Title=’Study Area 13F89’,$

C_annotation=[‘Low’,’Middle’,’High’], Levels=[200, 500, 800] 输出结果应与图19中的图例相似。

47

IDL IDL入门教程

图19: 等值线可以用自己提供的文本标识

改变等值线图的外观

修改等值线图的外观有许多方法。[www.loach.net.cn)这里有一些例子。能改变的特性之一是等值线的线型(见表3列出的可选用的线型值)。例如,为了使等值线成为虚线的线型,键入:

IDL> Contour, peak, lon, lat, Xstyle=1, Ystyle=1, $

/Follow, C_LineStyle=2

假如需要隔二条等值线有一条虚线,可以用C_LineStyle 关键字指定一个线型索引矢量,假如等值线数比索引号多,那么这些索引号将被循环使用或被重复使用。键入 :

IDL> contour, peak, lon, lat, XStyle=1, Ystyle=1, /Follow, $

Nlevels=9, C_LineStyle=[0, 0, 2] 输出结果应与图20相似.

图20:可以修改等值线图的许多方面。这是隔二条等值线有一条虚线的等值线图。

可以改变等值线的宽度。例如,要使等值线具有双倍的宽度,可键入:

IDL> Contour, peak, lon, lat, Xstyle=1, Ystyle=1, $

Nlevels=12, C_Thick=2, /Follow

通过指定一个线宽矢量,可以间隔修改等值线的宽度:

IDL> Contour, peak, lon, lat, Xstyle=1, Ystyle=1, $

Nlevels=12, C_Thick=[1, 2], /Follow

通过修改等值线图,可以很容易地看到等值线图的下坡方向。键入:

IDL> Contour, peak, lon, lat, Xstyle=1, Ystyle=1, $

/ Follow, Nlevels=12, /Downhill

48

IDL IDL入门教程

输出结果应与图21相似。[www.loach.net.cn)

图21:用Downhill来显示等值线图的下坡方向。

给等值线图赋色

给等值线图着色的方法有许多种。(颜色将在第83页的“IDL的颜色运用”中详细讨论。现在,只须按如下键入TvLCT命令即可,以后将学到这个命令意味着什么。实质上,装载了三个颜色矢量,每个矢量的三个分量分别代表颜色的三个组成部分红,绿,蓝。这三种颜色矢量为碳灰,黄,绿色。)假如需要一张以碳灰颜色为背景的黄色等值线图,可键入:

IDL> TvLCT, [70, 255], [70, 255], [70, 0], 1

IDL> Contour, peak, lon, lat, XStyle=1, YStyle=1, $

NLevels=10, Color=2, Background=1, /Follow

也可以用C_Color关键字给等值线本身单独添上颜色。如果需要将上图的等值线变为绿色 ,键入:

IDL> TvLCT, 0, 255, 0, 3

IDL> Contour, peak, lon, lat, Xstyle=1, Ystyle=1, /Follow, $

Nlevels=10, Color=2, Background=1, C_Colors=3

关键字C_Color也可以被表达为色彩表索引号的矢量,并以循环的方式绘制等值线。也可以使用Tek_Color命令为等值线创建或者装入颜色,如下:

IDL> Tek_Color

IDL> TvLCT, [70, 255], [70, 255], [70, 0], 1

IDL>Contour, peak, lon, lat, XStyle=1, YStyle=1, $

49

IDL IDL入门教程

Nlevels=10, Color=2, Background=1, $

C_Colors=IndGen(10)+2, /Follow

可以很容易地用C_Colors关键字使每间隔二条等值线有一条蓝色的等值线,其余的等值线为绿色。[www.loach.net.cn)键入: IDL> Contour, peak, lon, lat, Xstyle=1, Ystyle=1, $ NLevels=12, Color=2, Background=1, $ C_Colors=[3, 3, 4], /Follow 创建填充的等值线图

有时,不只是想观察等值线,也想看看填充后的等值线图。创建一张填充的等值线图,只需使用关键字Fill即可。首先,装入12种颜色于色彩表中作为填充颜色。色彩索引号由关键字C_Colors给出。键入:

IDL> LoadCT, 0

IDL> LoadCT, 4, Ncolors=12, Bottom=1

IDL> Contour, peak, lon, lat, Xstyle=1, Ystyle=1, /Fill, $

NLevels=12, /Follow, C_Colors=Indgen(12)+1

用这种方法填充颜色还是存在许多问题,尽管从显示看不是很明显。事实上,在等值线图有一个以背景颜色填充的”洞”。假如将背景色与图形颜色交换一下,就可以看得更清楚一些(事实上,PostScript中就是这样做的。这也是致使许多IDL程序人员焦头烂额的原因)。

IDL> Contour, peak, lon, lat, Xstyle=1, Ystyle=1, /Fill, $

NLevels=12, /Follow, C_Colors=Indgen(12)+1, $

Background=!P.Color, Color=!P.Background

“洞”产生的原因是由于IDL用第一种颜色填充了第一和第二条等值线间的空间。用第一种填充颜色去填充第零条(或背景)和第一条等值线之间的空间似乎更合理。但是,要使IDL这样做,不得不给定自己的等值线数目,并用关键字Levels传送给Contour命令。通常可用下述代码实现: IDL> step = (Max(peak) – Min(peak)) / 12.0

扩展:idl / idl教程 / idl程序设计

IDL> clevels = IndGen(12)*step + Min (peak)

现在,就得到了正确的等值线填充颜色。

IDL> Contour, peak, lon, lat, Xstyle=1, Ystyle=1, /Fill, $

Levels=clevels, /Follow, C_Colors=Indgen(12)+1, $

Background=!P.Color, Color=!P.Background

通常情况下,在填充等值线图时,经常定义等值线数目不失为一种好的方法。此外,要将填充的等值线图和色彩棒一起显示时,那么,创建自己的等值线数目是确保等值线数目与色彩棒的级数一致的唯一方法。

有时候,需要填充有丢失数据的等值线图或者是等值线超出了图形边界的等值线图,这种情况称为“开放的等值线”。 IDL处理这些开放的等值线时有时比较困难。填充这类等值线图的最好办法是使用关键字Cell_Fill,而不是使用 Fill 关键字。这将导致Contour命令使用单元填充算法。这种算法没有 Fill关键字使用的算法效率高,但在这种情况下可以获得更好的填充效果。假如需要将填充的等值线图放在地图投影上,使用Cell_Fill关键字也是个好主意。 IDL> Contour, peak, lon, lat, Xstyle=1, Ystyle=1, $

50

IDL IDL入门教程

Levels=clevels, C_Colors=Indgen(12)+1, /Cell_Fill

单元填充算法有时会破坏等值线图的坐标轴。(www.loach.net.cn)可以通过不带数据的等值线图的重新绘制来修复。键入:

IDL> Contour, peak, lon, lat, Xstyle=1, Ystyle=1, $

Levels=clevels, /NoData, /NoErase, /Follow

有时,

可能想在已填充好颜色的等值线图上看到等值线。在IDL中用Overplot关键字可以轻而易举地实现。键入:

IDL> Contour, peak, lon, lat, Xstyle=1, Ystyle=1, $

Levels=clevels, /Fill, C_Colors=IndGen(12)+1

IDL> Contour, peak, lon, lat, Xstyle=1, Ystyle=1, /Follow, $

Levels=clevels, /overplot

输出结果应与图22相似

图22:在已填充的等值线图上覆盖等值线

注意,不要混淆Overplot和NoErase关键字。它们是相似的,但确切地说是不一样的。在等值线图上,Overplot关键字仅仅绘出等值线,而不绘出等值线图的坐标轴。 NoErase关键字则是绘出完整的等值线图,而不删除在屏幕上已显示的内容。

在显示窗口定位图形输出

IDL有几种在显示窗口中定位线画图、曲面图、等值线图和其它图形的方法(比如地图投影)。 51

IDL IDL入门教程

为了理解 IDL 怎样定位图形,了解一些定义很重要。(www.loach.net.cn)图形位置是指在显示窗口上被图的坐标轴框 起来的部分中的位置。图形位置不包括坐标轴标识,坐标轴标题或者其它注释(见下面的图 23) 。 图形区域是显示窗口的一部分,包括图形位置,也包括环绕图形位置的空间,用来注明坐标轴标 识,坐标轴标题和图标题等。图形边界定义为在显示窗口内不包括图形位置的区域。 图形位置可以用!P.Position 系统变量设置,或者用 Position 关键字对 Plot, Surface, Contour 或其它 IDL 图形命令进行设置。整个图形区域可用!P.Region 系统变量设置,或者通 过!X, !Y 和!Z 系统变量的 Region 字段来设置单个坐标轴的区域。图形边界可以用[XYZ]Margin 关键字来对 Plot, Surface, Contour 或 IDL 的其它图形命令进行设置,或者通过!X, !Y 和!Z 系统变量的 Margin 字段来设置。 在缺省值情况下,IDL 是在将图形输出到显示窗口的时候设置图形边界的。但是,正如所看 到的,这并不是最好的选择。有时,使用图形定位来定位图形显示会更好,尤其是,当在一个显 10 示窗口中显示多个命令的输出结果时。 5 00 20 40 80 图形位置10 5 00 20 40 80 图形区域10 5 00 20 40 80 图形边缘图 23: 图形位置是被坐标轴包围起来的区域。图形区域与图形位置类似,但它还包括图形标题 和其它注释的区域。图形边缘正好与图形位置相反。图形边缘由字符的单位确定,而图形位置和 图形区域是由归一化的坐标单位确定。设置图形边缘图形边缘可以用图形命令中的[XYZ]Margin 关键字设置,或者通过!X,!Y 和 !Z 系统变量的 Margin 字段来设置。关于图形边缘的特殊地方在于根据字符尺寸来确定的单位。X 方向的边缘是52

IDL IDL入门教程

用两元素矢量来设置的,这两个元素分别规定左右的偏移量。[www.loach.net.cn]Y方向的边缘用同样的方法确定底部和顶部的偏移量。缺省边缘值是X轴方向为10和3,Y轴方向为4和2。为了查看当前字符尺寸的设备坐标值或像素坐标值,可键入:

IDL> Print, !D.X_Ch_Size, !D.Y_Ch_Size

例如,在苹果机(Macintosh)中,缺省的字符尺寸在X方向上为6个像素,在Y方向上为9个像素。因此,一张等值线图的边缘就被确定为图形的左边为60个像素(6*10),右边为18个像素(6*3)。如果CharacterSize关键字在 Contour命令中设置为2,那么将会出现图形的左边边缘为120个像素,而图形的右边边缘为36个像素。

例如,为了将图形四周边缘都改变为3个缺省的字符宽度,可键入:

IDL> Plot, time, curve, Xmargin=[3, 3], Ymargin=[3, 3]

注意,如果同时改变字符尺寸,图形将出现非常大的差异。因为图形边界是由字符的尺寸确定的。键入:

IDL> !X. Margin = [3, 3]

IDL> !Y. Margin = [3, 3]

IDL> Contour, peak, CharacterSize=2.5

IDL. Contour, peak, CharacterSize=1.5

假如用其它的字符尺寸来做一些同样的操作,会发现,字符尺寸越大,字符将变得很大并且图形部分将变得很小,这并不是所希望看到的。当向下继续学习时,请确保将图形边界已恢复为缺省值。键入:

IDL> !X.Margin = [10, 3]

IDL> !Y.Margin = [4, 2]

注意,不象许多其它系统变量通过将其设置为零即可恢复其缺省值那样,系统边缘变量则必须直接将其设置为缺省值。假如没有键入以上的两条命令,现在就键入。

设置图形位置

设置图形位置需要设置一个四个元素的矢量,该矢量依次给定图形在显示窗口中的左下角和右上角坐标[X0,Y0,X1,Y1]。这些坐标值通常为归一化的值,其范围在0至1之间(如:0常常代表显示窗口的左边或者底部,1常常代表显示窗口的右边或者顶部。)

扩展:idl / idl教程 / idl程序设计

设想需要将图形输出结果在显示窗口的上半部分显示,可以按如下设置!P.Position系统变量并显示图形:

IDL> !P.Position = [0.1, 0.5, 0.9, 0.9]

IDL> Plot, time, curve

所有后面的图形输出定位方法都是类似的。将!P.Position系统变量复位,以便后面的图形输出能正常地显示在窗口中。键入:

IDL> !p.position = 0

假如仅想给一张图形显示定位,可以用图形命令的Position关键字规定一个图形位置。如果要在整个显示窗口的左半部分显示等值线图,可以键入:

53

IDL IDL入门教程

IDL> Contour, peak, Position=[0.1, 0.1, 0.5, 0.9]

注意,Position关键字可以用来在相同的显示窗口输入多幅图形。(www.loach.net.cn]只要确保在输入第二幅图形和所有的后续图形时,使用NoErase关键字。这可防止在显示图形时删除前面已显示的图形。对于所有的图形输出命令来说,这是一项缺省特性,但是TV和TVScl命令是例外。

在一张等值线图上加入一条线图,可键入:

IDL> Plot, time, curve, Position=[0.1, 0.55, 0.95, 0.95]

IDL> Contour, peak, Position=[0.1, 0.1, 0.95, 0.45], /NoErase

设置图形区域

图形区域与图形位置一样,都是由归一化坐标值来确定的。同样可以通过设置!P.Region系统变量来指定。由于不存在和其它图形命令等效的关键字,因此设置图形区域没有设置图形位置方便。如果希望后续图形能正常地使用整个显示窗口,应确保已经将系统变量复位了。例如,在显示窗口上方三分之二的部分区域中显示一幅图形,键入:

IDL> !P.Region = [0.1, 0.33, 0.9, 0.9]

IDL> Plot, time, curve

将!P.Region系统变量复位,以便后续图形能正常地在窗口内显示。键入:

IDL> !P,Region = 0

创建多个图形

正如所见,通过使用图形位置和图形区域系统变量以及上面所讨论的关键字可以在一个显示窗口中定位多个图形(只要绘制第二个和后续的图形时使用了NoErase关键字)。但是使用!P.Multi系统变量在显示窗口内创建多个图形更加容易。!P.Multi由以下五个元素的矢量定义。

!P.Multi[0] !P.Multi的第一个元素包括剩下的要在显示窗口或者PostScript页上绘制的图

形数目。这有点不直观,以下就可以看到它是如何使用的。通常设置为0,

意思是,没有剩下要在显示窗口输出的图形。接下来的图形命令将删除显示

的图形,并且开始绘制新的多个图形中的第一个。

!P.Multi[1] 此元素规定了该页上图形的列数

!P.Multi[2] 此元素规定了该页上图形的行数

!P.Multi[3] 此元素规定了在Z方向上叠加的图形数目(仅适用已经建立了三维坐标系的情

况下)

!P.Multi[4] 此元素规定了是先按行显示图形(!P.Multi[4]=0),还是先按列显示图形

(!P.Multi[4]=1)。

54

IDL IDL入门教程

假如想将!P.Multi

参数设置为按两行两列在显示窗口内显示四幅图形,并且,先按列显示图形,键入: IDL> !P.Multi = [0, 2, 2, 0, 1] 显示图形时,如果要求每个图形占据窗口的四分之一位置,键入:

IDL> window, Xsize=500, Ysize=500

IDL> Plot, time, curve, LineStyle=0

IDL> Contour, peak, lon, lat, Xstyle=1, Ystyle=1, Nlevels=10

IDL> Surface, peak, lon, lat

IDL> shade_Surf, peak, lon, lat

输出结果应与图24相似

图24: 在单个显示窗口内可以绘制多幅图形

给多幅图形的图留下标题空间

当IDL计算图形位置时,是用整个显示窗口来决定每幅图形的大小。[www.loach.net.cn)但是,有时想在显示窗口上有额外的空间来放图形标题或者其它类型的注释。可以通过使用!X,!Y和!Z!系统变量的“外 55

IDL IDL入门教程

边缘”字段为多幅图形留出空间。(www.loach.net.cn]外边缘字段仅仅在 P.Multi系统变量被使用时有效。它们与正常的图形边缘一样,也是按字符单位来计算的。

如果想为刚刚创建好的四个图形的总图加上一个标题,应为标题留出空间。键入: IDL> !P.Multi = [0, 2, 2, 0, 1]

IDL> !Y.Omargin = [2, 4]

IDL> Plot, time, curve, LineStyle=0

IDL> Contour, peak, lon, lat, Xstyle=1, Ystyle=1, Nlevels=10

IDL> Surface, peak, lon, lat

IDL> Shade_Surf, peak, lon, lat

IDL> XYOuts, 0.5, 0.9, /Normal, ‘Four Graphics Plots’, $

Alignment=0.5, Charsize=2.5 输出结果应于图25相似。

图25: 使用关键字!Y.OMargin在多幅图形的上方留出4个字符高度的空间来放标题 使用!P.Multi变量创建不对称的排列

使用!P.Multi变量绘图没有必要是对称排列。例如,需要曲面图与阴影图一上一下地排列显 56

IDL IDL入门教程

示在显示窗口的左边,而显示窗口的右边是一张用同样数据生成的等值线图。[www.loach.net.cn]可键入: IDL> !P.Multi = [0, 2, 2, 0, 1]

IDL> !Y.OMargin=[0,0]

IDL> Surface, peak, lon, lat

IDL> Shade_Surf, peak, lon, lat

IDL> !P.Multi = [1, 2, 1, 0, 0] IDL> Contour, peak, lon, lat, Xstyle=1, Ystyle=1, Nlevels=10

第一个!P.Multi命令设置了一个二列二行的排列形式,第一、第二张图已制好。第二个 !P.Multi命令设置了一个二列一行的排列形式。但要注意 !P.Multi[0]被设置为1。结果是等值线图进入了显示窗口的第二个位置而不是第一个。结果由图26可以看出。

图26: 可以使用!P.Multi在显示窗口定位图形的不对称排列

扩展:idl / idl教程 / idl程序设计

注意:与PLOT和CONTOUR命令不一样,TV命令与!P.Multi一起使用无效。但是,在此书中可以用TVImage程序代替TV命令,该程序在已经下载的程序中。如果设置了MULTI关键字,!P.Multi系统变量就有效。试试这些命令:

IDL> image = LoadData(7)

IDL> !P.Multi=[0, 2, 2]

IDL> FOR j=0, 3 DO TVImage, image, /Multi

57

IDL IDL入门教程

确保已经将!P.Multi复位,以便在一页上显示单个图形。[www.loach.net.cn)象许多系统变量一样, !P.Multi可以通过设置!P.Multi=0重新设置为它的缺省值。

IDL> !P.Multi = 0

给图形显示添加文本

图形注释和其它文本可以通过许多方式添加到图形显示上。最通常的方法是通过图形显示命令的关键字。被添加的文本可以三种字体“风格”中的任意一种形式出现:矢量字体(有时也称为软字体或 Hershey 字体)、TrueType字体、硬字体。字体类型可以根据表6通过设置!P.Font系统变量或者在图形输出命令中设置 Font关键字来加以选择。 !P.Font

-1

1 字体选择 矢量字体(也叫软字体或Hershey字体) 硬字体 TureType轮廓字体

表6: 字体“风格”可以通过设置!P.Font系统变量或者Font关键字为适当值来加以选择。矢

量字体是直接图形命令的缺省字体,它们有不依赖于平台的优点

在缺省值情况下,直接图形程序使用的是矢量或软字体的形式。矢量字体由矢量坐标描述。其结果是,它们是独立于平台并且极易在三维空间旋转。但是,许多人发现,对于高质量的硬拷贝输出来说,矢量字体太“瘦”了。为此,需要更丰满的字体(比如:TrueType字体或者PostScript打印机硬件字体)。通过设置!P.Font系统参数为-1或者在图形输出命令上设置Font关键字为-1,就选择矢量字体了。如:

IDL> Plot, time, curve, Font=-1, Xtitle=’Time’, $

Ytitle=’Signal’, Title=’Experiment 35F3a’

TrueType字体也称为轮廓字体,这种字体由一系列的外形轮廓描述的,这些轮廓通过创建一系列的多边形来填充。IDL有四种TrueType字体家族系列:Times, Helvetica, Courier, 和Symbol。TrueType字体渲染需要花更长的时间,因为这种字体首先必须刻绘出来,然后创建相应的填充多边形,最后填充。并且许多人发现这种字体在低分辨率显示器上用小磅值时显示效果不好。但是它们有可以旋转的优点,并且硬拷贝输出上较美观。TrueType字体是IDL对象图形系统的一种缺省字体。

用缺省的Helvetica TrueType字体的外形来绘制图形,须设置Font关键字为1。如: IDL> Plot, time, curve, Font=1, Xtitle=’Time’, $

Ytitle=’Signal’, Title=’Experiment 35F3a’

TrueType字体可以用Device命令通过Set_Font和TT_Font关键字来选择。如下: IDL> Device, Set-Font=’Courier’, /TT_Font

IDL> Plot, time, curve, Font=1, Xtitle=’Time’, $

Ytitle=’Signal’, Title=’Experiment 35F3a’

在IDL中,为了掌握更多的TrueType字体,可以使用联机帮助系统。

IDL> ? fonts

58

IDL IDL入门教程

硬字体通过设置!P.Font系统变量或 Font关键字为0来加以选择。(www.loach.net.cn]通常情况下,硬字体并不用于图形显示中,而是在当内容被输出到硬拷贝输出设备时使用,例如 PostScript打印机。直到最近的IDL版本,硬字体都不能很好地在三维空间内旋转。因此,在使用类似于Surface等三维命令时,一般都不使用硬字体。

IDL> Plot, time, curve, Font=0, Xtitle=’Time’, $

Xtitle=’Signal’, Title=’Experiment 35F3a’

找出可用字体的名称

可以用以下Device命令找出可用的硬字体名。如:

IDL> Device, Font=’*’, Get_FontNames=fontnames

IDL> For j=0, N_Elements(fontnames)-1 DO Print, fontnames[j]

只要使用TT_Font关键字,TureType字体名称可用类似的方法找出。TT_Font关键字用来选择系统上可用的TureType字体。(可以把自己的TureType字体加到IDL提供的四种系列类型内。如何实现可参考IDL的联机帮助系统。)

IDL> Device, FONT=’*’, Get_FontNames=fontnames, /TT-Font

IDL> For j=0, N_Elements(fontnames)-1 DO Print, fontnames[j]

可用的矢量字体名称在表7给出。

用XYOutS命令添加文本

在IDL 中一个非常重要的命令是XYOutS命令(“在XY 给定的位置,输出一个字符串”)。这个命令用来在窗口的特定位置放入一个文本字符串。(XYOutS 的第一个位置参数是X的位置,第二个位置参数是Y的位置)。例如,给线画图加上一个较大的标题,键入如下命令:

IDL> Plot, time, curve, Position=[0.15, 0.15, 0.95, 0.85]

IDL> XYOutS, 0.5, 32, ‘Results: Expe riment 35F3a’, Size=2.0

注意,是用数据坐标来给定X和Y的位置,同时Y坐标在图形边界之外。在缺省的情况下,XYOutS过程使用数据坐标系统。但是,如果选用适当的关键字,设备坐标系统 和归一化的坐标系统也可使用。

(数据坐标系统自然地由其自身描述。设备坐标有时称为像素坐标,设备坐标系统经常和图像一起使用。归一化的坐标系统在每个方向从0到1。当需要用独立于设备输出图形时,经常使用归一化坐标。)

例如,可以象如下使用归一化坐标把标题加在线画图上。当编写IDL程序时,用归一化坐标确定标题和其它注释等是一种很好的主意。这不仅更容易于在显示窗口定为图形,也便于在PostScript和其它硬拷贝输出文件中定位图形。

IDL> Plot, time, curve, Position=[0.15, 0.15, 0.95, 0.85]

59

IDL IDL入门教程

IDL> XYOutS, 0.2, 0.92, ‘Results: Experiment 35F3a’, $ Size=2.0, /Normal

数值 !3 !4 !5 !6 !7 !8 !9 !10 !11

描述 Simplex Roman Simplex Greek Duplex Roman Complex Roman Complex Greek Complex Italian Math Font Special CharactersGothic English

扩展:idl / idl教程 / idl程序设计

数值 !12 !13 !14 !15 !16 !17 !18 !20 !X

描述 Simplex Script Complex Script Gothic Italian Gothic German Cyrillic Triplex Roman Triplex Italian Miscellaneous 回到刚进入时的字体

表7:Hershey字体和其相应的在IDL中用于选择各字体的索引号

用矢量字体使用XYOut

XYOutS命令可用于矢量字体,TureType字体或硬字体,只需按上述的那样,简单的设置Font关键字值即可。[www.loach.net.cn)这儿的讨论是关于矢量字体的,因为在直接图形命令中,此字体系统使用最频繁。表7中给出可获得的矢量字体和其相应的索引号,可以通过索引号来选择的特定字体。

矢量字体或Hershey字体的主要优点是它们的平台独立性,并且可在三维空间中缩放和旋转。例如,可以用Triplex Roman字体输出上图的标题,键入:

IDL> Plot, time, curve, Position=[0.15, 0.15, 0.95, 0.85]

IDL> XYOutS, 0.2, 0.92, ‘!17Results: Experiment 35F3a!X’, $ Size=2.0, /Normal

Triplex Roman字体由!17转义序列来选定。标题串末端的!X将使字体转变为Simplex Roman字体,而Simplex Roman字体是在变为Triplex Roman字体前所使用的字体。这个转变步骤是非常重要的。否则,缺省设置将变为Triplex Roman,并且所有接下来的串标记都将使用Triplex Roman字体。试试使用 Greek字符集作为X轴的标题,并且按下面输出下图的标题。键入: IDL> Plot, time, curve, Xtitle=’!17w’, $ Position=[0.15, 0.15, 0.95, 0.85]

IDL> XYOutS, 0.2, 0.92, ‘Experiment 35F3X’, size=2.0, /Normal

可以在图27中看到结果。现在留心,即使没有规定该图的标题什么字符集,标题也是用Greek字符集输出的。恢复为缺省项Simplex Roman的唯一办法是用显式地使用 Simplex Roman字体输出另一个字符串,例如:

IDL> XYOutS, 0.5, 0.5, ‘!3Junk’, /Normal, CharSize=-1

60

IDL IDL入门教程

注意,在上面的代码中CharSize关键字的使用。(www.loach.net.cn]当这个关键字值为-1时,字符串被隐藏,不在窗口显示。

图27:当选择一种Hershey字体要注意,否则可能为用户提供一个看上去象希腊字母的标题 排列文本

可以用XYOutS

命令的Alignment关键字通过相对位置来定位文本。当Alignment的值为0时,字符串居左排列(这是缺省值);当Alignment的值为1时,字符串居右排列;当Alignment的值为0.5时,将根据X和Y值所定义的位置居中排列。例如:

IDL> Window, Xsize=300, Ysize=250

IDL> XYOutS, 150, 55, ‘Research’, Alignment=0.0, $

/Device, CharSIZE=2.0

IDL> XYOutS, 150, 110, ‘Research’, Alignment=.5, $

/Device, CharSIZE=2.0

IDL> XYOutS, 150, 170, ‘Research’, Alignment=1.0, $

/Device, CharSize=2.0

IDL> Plots, [0.5,0.5], [1.0,0.0], /Normal

删除文本

用XYOutS书写的文本有时可以通过用背景颜色书写同样的文本来删除。Color关键字与!P.Background系统变量一起使用可以达到这个目的。需要指出的是,这仅仅在文本只是写在背景上没有任何东西的情况下奏效。通常还有别的更有效的方法来删除注释。(可参见118页的“从 61

IDL IDL入门教程

显示窗口删除注释”的例子)。(www.loach.net.cn]为了明白如何用背景颜色删除注释,键入:

IDL> window, Xsize=300, Ysize=250

IDL> XYOutS, 150, 110, ‘Research’, Alignment=0.50, $

/Device, CharSize=2.0

IDL> XYOutS, 150, 110, ‘Research’ Alignment=0.50, $

/Device, CharSize=2.0, Color=!P.Background

改变文本的方向

用XYOutS命令输出的文本可以通过Orientation关键字相对于水平方向上的角度来定向。Orientation关键字可确定文本基线从水平基线开始旋转的度数。键入:

IDL> Window,Xsize=300, Ysize=250

IDL> XYOutS, 150, 110, ‘Research’, Alignment=0.5, $

/Device, CharSize=2.0, Orientation=45

IDL> XYOutS, 150,180, ‘Research’, Alignment=0.50, $

/Device, CharSize=2.0, Orientation=-45

给图形显示添加线和符号

给图形添加注释的另一种有效程序是PlotS命令,它是用来在图形显示上添加符号或线条。PlotS命令可在二维或三维空间中使用。

用PlotS程序画线,只需简单地提供含有X和Y坐标的矢量即可,矢量中的X、Y值是需要连接的点的X、Y坐标值。例如,从点(0,15)到点(6,15)在线画图上画一条基线,键入: IDL> Window, XSize=500, YSize=400

IDL> Plot, time, curve

IDL> PlotS, [0,6], [15,15], LineStyle=2

输出结果应与图28相似。

PlotS程序可以用来在任何位置标上符号。下面是在曲线上每五个点处标注一个菱形符号的实例。

IDL> TvLCT, [70, 255, 0], [70, 255, 250], [70, 0, 0], 1

IDL> Plot, time, curve, Background=1, Color=2

IDL> index = IndGen(20)*5

IDL> Plots, time[index], curve[index], Psym=4, $

Color=3, SymSize=2

62

IDL IDL入门教程

图28:用PlotS 命令画一条虚线跨过图形的中部

PlotS命令也可以用来在图上重要信息的周围画出一个方框。[www.loach.net.cn]通过PlotS命令与其它图形命令组合,如XYOutS命令,可以有效地注释图形显示。例:

IDL> TvLCT, [70, 255,0], [70,255,255], [70, 0, 0],1

IDL> Plot, time, curve, Background=1, Color=2

IDL> box_x_coords = [0.4, 0.4, 0.6, 0.6, 0.4]

IDL> box_x_coords = [0.4, 0.6, 0.6, 0.4,0.4]

IDL> PlotS, box_x_coords, box_y_coords, Color=3, /Normal

IDL> XYOutS, 0.5, 0.3, ‘Critical Zone’, Color=3, Size=2, $

扩展:idl / idl教程 / idl程序设计

Alignment = 0.5, /Normal

注意,可以容易地使用XYOutS和PlotS命令为图形显示创建图例。

图形显示添加色彩

另一种有效的标注图形显示的方法是使用颜色。Polyfill命令是一个低级的图形显示命令,它可用特殊的颜色或图案填充任意形状的多边形(无论是在二维或还是在三维环境中定义的)。例

如,可以使用Polyfill命令用红颜色填充上面线画图中方框:

IDL> TvLCT, 255, 0,0 ,4

IDL> Erase, Color=1

IDL> Polyfill, box_x_coords, box_y_coords, Color=4, /Normal

IDL> Plot, time, curve, Background=1, Color=2, /NoErase

63

IDL IDL入门教程

IDL> PlotS, Box_x_coords, box_y_coords, Color=3, /Normal

IDL> XYOutS, 0.5, 0.3, ‘Critical Zone’, Color=3, Size=2, $

Alignment = 0.5, / Normal

颜色有时代表一个数据集的另外一维的特性。(www.loach.net.cn]例如,可以二维圆形(或多边形)显示XY数据,而每个多边形的颜色就可表现出数据的某些附加特性,比如温度和人口密度等。看看是如何实现的。

IDL没有构建圆的模块,但是很容易编写这样一个功能模块。打开文本编辑器,键入代码来创建IDL的Circle功能。

FUNCTION CIRCLE, xcenter, ycenter, radius

Points = (2 * ! PI / 99.0) * FindGen(100)

x = xcenter + radius * Cos(points)

y = ycenter + radius * Sin(points)

RETURN, Transpose([x],[y])

END

组成圆周的X和Y值将以2*100数组形式返回。可以将该数组输入到Polyfill命令中。以Circle.pro保存该程序,并通过键入如下命令进行编译:

IDL> .Compile circle

然后,创建随机分布的X和Y数据。(将Seed设置回初始状态,这样输出结果将与图29看上去相似)。键入:

IDL> seed = -3L

IDL> x = RandomU(seed,30)

IDL> y = RandomU(seed,30)

将Z值设为这些X值和Y值的函数。键入:

IDL> z = (3 * ( (x-0.5)^2) + 5*((y-0.25)^2)) * 1000

打开窗口绘制XY位置,就可以看到这些数据是怎样以随机形式分布的。键入:

IDL> Window, Xsize=400, Ysize=350

IDL> Plot, x, y, Psym=4, Position=[0.15, 0.15, 0.75, 0.95],$

Xtitle=’X Locations’, Ytitle=’Y Locations’

将以不同颜色的圆显示与XY位置相关的Z数据。需要加入一张颜色表,并且使Z数据缩放至可获得的颜色数的范围内。键入:

IDL> LoadCT, 2

IDL> zcolors = Bytscl(z, Top=!D.Table_Size-1)

在这个例子里使用的Circle程序有许多弱点。主要缺点是它并非总是生成圆。假如用数据坐标系统来给定圆的坐标,圆形可能将以椭圆的形式显示,主要取决于图形长宽比例以及其它影响因素。(要获得非常棒的圆,可以从NASA Goddard Astrophysics的IDL例库中下载TVCircle程 64

IDL IDL入门教程

序,可以用浏览器通过来找到该例库)。[www.loach.net.cn]为避免Circle程序中的这种不足,可用Convert_Coord命令将数据坐标转换为设备坐标。键入:

IDL> coords = Convert_Coord (X, Y, /Data, /To_Device)

IDL> x = coords(0,*)

IDL> y = coords(1,*)

最后需要使用Polyfill命令画出表示Z数据的彩色圆。键入:

IDL> For j=0, 29 Do Polyfill, Circle(x(j), y(j), 10), $

/Fill, Color=zcolors(j), /Device

附带地说一下,最好有一个色棒能够告知Z值和各种颜色的某些关系。可以用本书的Colorbar程序增加一个色棒,键入:

IDL> Colorbar, Position = [0.85, 0.15, 0.90, 0.95], $

Range=[Min(z), Max(z)], /Vertical, $

Format=’(I5)’, /Right, Title=’Z Values’ 输出结果应与图29相似

图29:在二维图中圆的颜色代表了第三维信息

65

IDL IDL入门教程

平滑图像

可以通过将每个像素值与它周围相邻像素值进行平均来平滑图像。[www.loach.net.cn)这就是均值或方盒平滑。均值平滑是由IDL中的功能函数Smooth完成的,它是在给定的奇数宽度的范围内实现等加权值平滑。例如,如果周围是3*3宽度,那么每个像素由它和它的周围八个像素值的平均值代替。 比较一幅没有经过平滑处理的图像和经过5*5 方盒的均值平滑处理后的图像,键入: IDL>Window,0,XSize=192*3,YSize=192

IDL>TV,image,0,0

IDL>smoothed=Smooth(image,5,/Edge_Truncate)

IDL>TV, smoothed, 192, 0

注意,与命令Smooth一起使用的关键字Edge_Truncate。该关键字可复制图像边缘附近的像素,以便实现整幅图像的平滑。如果不使用该关键字,图像边缘附近的像素仅仅是简单复制,而没有平滑。

图像平滑被应用在一种称作晕光蒙片的图像处理技术中。这种技术可用作定位图像上的棱边或者是像素值突然变化的地方。这种技术非常简单:从未平滑的图像中减去平滑的图像即可。键入:

IDL>TV, ((image-smoothed)+255)/2.0, 2*192, 0

图像显示应如图36所示。

用Smooth命令,赋给相邻的像素值相等的权值来计算平均值。有时会导致出现不希望的模糊图像。另一种方式是用称为卷积的过程来平滑图像。这种技术中,一个方形内核和图像一起参与卷积计算。例如,在3*3的情况下,Smooth命令使用的内核为:

1 1 1

1 1 1

1 1 1

如果给予中心像素值更大的权值,而它周围像素值的权值小一些,图像就不会那么模糊了。例如,可以创建如下的一个核心:

1 2 1

2 8 2

1 2 1

通过Convol命令用上述内核对图像进行卷积处理,键入:

IDL>kernel=[[1,2,1], [2,8,2], [1,2,1]]

扩展:idl / idl教程 / idl程序设计

IDL>TV, image, 0, 0

IDL>TV, Smooth(image, 3, /Edge_Truncate), 192, 0

IDL>TV, Convol(image, kernel, Total(kernel), $

/Edge_Truncate), 2*192, 0

66

IDL IDL入门教程

图36:左边为原始图像,中间为平滑处理过的图像,右边为经晕光蒙片处理后的图像。(www.loach.net.cn]

当然,可以创建任意大小的内核。如下是一个典型高斯分布的5*5内核:

1 2 3 2 1

2 7 11 7 2

3 11 17 11 3

2 7 11 7 2

1 2 3 2 1

可将上述内核应用于图像处理:

IDL>kernel=[[1,2,3,2,1], [2,7,11,7,2], [3,11,17,11,3], $

[2,7,11,7,2], [1,2,3,2,1]]

IDL>TV, Convol(image, kernel, Total(kernel), $

/Edge_Truncate), 192*2, 0

消除图像噪声

将图像上的噪声消除是一种常规的图像处理技术。噪声来自许多方面,它经常降低图像质量。噪声的一般表现形式是黑白点相间噪声,其中一些随机的像素有极端的像素值。要了解图像平滑是怎样剔除这种噪声的,首先需要创建一幅噪声图像。用以前的图像,并键入如下的命令,将10%的像素转换为黑白点相间噪声: IDL>noisy=image IDL>points=RandomU(seed, 1800)*192*192 IDL>noisy(points)=255 IDL>points=RandomU(seed, 1800)*192*192 IDL>noisy(points)=0 在原始图像的旁边创建一个窗口并显示噪声图像: IDL>Window,XSize=192*3,YSize=192 IDL>TV,image,0,0 IDL>TV, noisy, 192, 0 IDL中的Median命令是从图像上消除黑白点相间噪声的很好选择。Median命令与Smooth命令类似。不同之处是Median命令计算相邻像素的中间值,而不是平均值。这就有两个重要作用。第一,它能删除图像中的极端值。第二,它不使那些尺寸比邻域范围大的图像棱边或特征变模糊。要看是如何工作的,键入:

IDL>TV, Median(noisy, 3), 2*192, 0

67

IDL IDL入门教程

图形显示应如图37

所示。[www.loach.net.cn]

图37:左边为原始图像,中间为噪声图像,右边为用中值滤波器平滑处理后的噪声图像。 增强图像棱边

一个图像可以锐化或通过微分来增强图像棱边。IDL提供了两个做好的棱边增强函数:Roberts和

Sobel。还有一些其它方法也可用来增强图像棱边。例如,可以用拉普拉斯算子来和图像做卷积: 1 1 1

1 -7 1

1 1 1

因为改进了图像棱边的对比度,这也常常被称为拉普拉斯(Laplacian)锐化操作。需要了解这些方法是如何工作的,可键入:

IDL>TV, Sobel(image), 0

IDL>TV, Roberts(image), 1

IDL>kernel=[[1,1,1], [1,-7,1], [1,1,1]]

IDL>TV, Convol(image, kernel), 2

图形显示应如图38所示。

图38:三种增强图像棱边的方式。左边用的是Sobel方法。中间用的是Roberts方法。

右边是用拉普拉斯算子对图像做卷积。

图像的频域滤波

频域滤波是常规的图像和信号处理技术。它可以用来平滑处理图像,锐化图像,降低图像的模糊程度,和恢复图像。

68

IDL IDL入门教程

频率域滤波有如下三个基本步骤:

1. 用快速傅里叶变换(FFT)将图像从空间域转变为频域。(www.loach.net.cn]

2. 将转换后的图像乘以一个频率滤波器。

3. 将滤波后的图像返回为空间域。

这些步骤在IDL中是用快速傅里叶变换(FFT函数)完成的。(如果命令FFT的第二个定位参数为-1,则图像由空间域转变为频域。如果参数为1,则图像将相反转化)。频率滤波命令的一般形式如下:

filtered_image=FFT(FFT(image, -1)*filter, 1) 在这种情况中,image既可是一维矢量,也可以是一幅二维图像。滤波器是用来滤波图像中某些特定频率的一维矢量或二维数组。下面将详细介绍。

创建图像滤波器 在IDL中用基于数组的操作和函数很容易创建图像数字滤波器。许多普通的滤波器利用了所谓的频率图像或欧氏距离图的优点。一幅二维图像的欧氏距离图是一个与图像有同样大小的数组。距离图的每个像素被赋给一个值,这个值等于它到二维数组最近的角的距离。在IDL中的Dist命令用作创建欧氏距离图或频率图像。要观看一幅简单的距离图,可键入:

IDL>Surface,Dist(40)

在频域滤波中所用的滤波器一般为Butterworth频率滤波器。如下的方程给出一个低过Butterworth频率滤波器一般形式:

2nfilter=1/[1+C(R/R0)]

其中,常量C等于1.0或0.414[这个值将滤波器的幅度在R=R0时定义为50%或1/Sqrt(2)],R

,n是滤波器的阶数,通常为为频率图像,R0为给定的滤波器截止频率(实际中由像素宽度代替)

1。

高过Butterworth滤波器由如方程给出:

2nfilter=1/[1+C(R0/ R)]

要将频域滤波器应用到图像中,可用命令LoadData来打开图像Earth Mantle Convection。这是一个248*248的二维数组。

IDL>convec=LoadData(11)

键入如下命令来打开一个窗口,装入颜色表Standard Gramma II,并在左上角显示原始图像: IDL>Window,0,XSize=248*2,YSize=248*2

IDL>LoadCT,5

IDL>TV,convec,0,248

在频域滤波中的第一步是用函数FFT将图像由空间域转换为频率域,键入:

IDL>freqDomainImage=FFT(convec,-1)

通常,低频项代表一般的图像形状,高频项对图像增加细节。浏览频率域的图像通常是没有意义的,但有时对观察频域图像的功率谱有用。

功率谱是一幅频域图像中不同组成部分的幅度图。与源点(通常代表图像的中心)不同的距离代表不同的频率,相对源点不同的方向代表在原图像特征的不同方向。每个位置的功率表明该频率的大小和以在图像中的方向。功率谱对分离图像中的周期性结构或噪声是特别有用的。功率谱的幅度通常用对数座标表示,因为功率从某个频率到下一频率的变化非常大。

扩展:idl / idl教程 / idl程序设计

计算出这幅对流图像的功率谱,并将其显示在原始图的相邻位置上,可键入:

IDL>power=Shift(Alog(Abs(freqDomainImage)),124,124)

IDL>TV,power,248,248

69

IDL IDL入门教程

功率谱中的对称性表明这个图像上在越来越多的频率中包含了许多周期性结构。(www.loach.net.cn]输出应该类似于图39)。这个练习的目的是过滤掉图像中较高的频率。

接下来的一步是用频率滤波器转换图像。ButterWorth低通滤波器用于滤出图像内的高频成分。这些高频成分为图像提供详细信息,所以最终的结果是完成图像的平滑处理。创建低通频率滤波器可键入:

^IDL>filter = 1.0 / (1.0D + Dist(248)/15.0)2

注意,截止频率宽度是15个像素。这是足以删除高频的一半。在图39中功率谱可以看到。 使用该频率滤波器,再将图像由频率域转换回空间域,最后显示滤波后的图像。键入:

IDL>filtered = FFT(freqDomainImge * filter, 1) IDL>TV, filtered, 0, 0 为了让自己看到滤掉高频成分的图像,可以显示滤波后图像的功率谱,并在其旁显示滤波后的图像,键入:

IDL>filteredFreqImg = FFT(filtered, -1)

IDL>power = shift(Alog(Abs(filteredFreqImg)), 124, 124)

IDL>TV, power, 248,0

70

IDL IDL入门教程

图39:频域滤波器的图解。(www.loach.net.cn)在图的上半部分是没有滤波的图像,左边是它的功率谱。在图的下半部分是滤波后的图像,相邻的左边是它的功率谱。注意,大约一半高频成分在滤波后的图像中已经被消除,消除了很多图像细节信息,也平滑了图像。

71

IDL IDL入门教程

第三章 图像数据处理

本章概要

IDL最开始是一种处理图像的语言。[www.loach.net.cn)正因为此,世界各地的许多科学家和工程师仍在用IDL语言。这章阐述了图像处理的基本工作。将从中学到以下几点

1. 怎样读取和显示图像数据

2. 怎样缩放图像数据

3. 怎样在显示窗口中定位图像

4. 怎样改变图像的大小

5. 怎样从显示设备中读取图像

6. 怎样完成基本的图像处理任务

7. 怎样建立简单的图像滤波器

图像处理

事实上,任何类型的二维数据集都可认为是一幅图像。但是要在一个8位的显示设备上显示图像数据,就必须将图像数据调整为 0~255之间的字节型数值。(在一个24-bit的显示设备上,24位图像的RGB值必须调整成字节型数值。)因为图像总是以字节型数值显示,所以图像总是以字节型数组来存储。但是无论图像是怎样存储的,图像总是由两个显示图像的IDL命令:TV和TVScl以字节型数值来显示。

要了解是怎样工作的,需要有一些图像数据用于处理。用命令LoadData来打开图像数据集Ali and Dave。将要处理这两幅图像数据中的第二幅图像。键入

IDL>image=LoadData(10)

IDL>image=image[*,*,1]

显示图像

可用TV和TVScl两个IDL命令中的任一个来显示图像。这两个命令几乎在各个方面都是一样的,包括能与之一起使用的关键字。仅仅在一个方面不同:TVScl将图像数据调整为与IDL运行时所用颜色数目相适应的字节型数值。例如:如果在使用IDL时用220种颜色,则在图像显示之前TVScl将图像数据调整为0~219之间的字节型数值。

另一方面,TV命令取图像数据本身的值,仅仅将其作为字节型数值传送到显示设备上。如果有必要,图像数据将被截断以符合字节型数值。如果图像数据不被调整到0~255之间,图像将很可能显示不正确。

注意,与Plot,Surface和Contour命令不同,TV和TVScl命令在显示图像之前不删除已显示的内容。一般情况下这个问题不大,但有时候也会产生一些麻烦。如果想要一个干净的显示窗口来显示图像数据,无论当前窗口上的显示内容是什么,都可用一个简单的命令Erase来删除。 IDL>Erase

72

IDL IDL入门教程

这里有一个实例。(www.loach.net.cn)刚才读取的IDL的图像数据集已经调整在0~255之间。

可以键入如下内容来查看:

IDL>Print,Max(image),Min(image)

但是,如果在一个8位显示设备上工作,可能没有全部使用在显示器上可用的256种颜色。如果需要了解正在使用多少种颜色,可键入:

IDL>Print,!D.Table_Size

在一个8位显示器(这里指颜色表的大小)上,运行IDL时所用颜色的数目通常是在210~240之间,显然可用的颜色太少了。在一个24位的显示器上,可以获得1670000种颜色,但颜色表大小仍然是256。以后将会学到IDL是怎样选择它所用的颜色数目。

打开一个显示窗口,装上灰度颜色表, 用TV命令显示图像:

IDL>Window,0,XSize=192,YSize=192

IDL>LoadCT,0

IDL>TV,image

所得图像应如图30

所示。

图30:IDL和Research Systems公司的创始人--David Stern的图像。People.dat数据集中的另外一幅图像是Ali Bahrami,Research Systems公司的第一为员工。他们两个依然致力

于IDL的开发。

因为是用TV命令,所以数据没有经过调整就被传送到显示器中。尽管看不出来,但图像上所有大于IDL运行时的颜色数目的像素值都被设为同样的值。也就是说,比!D.Table_Size-1值大的像素被以相同的颜色显示。(在这种情况下,看到的颜色是灰色明暗图。)

如果用TVScl命令显示图像,也许能看出差别。打开另一个窗口并将其移到第一个窗口的旁边。用TVScl命令显示图像:

IDL>window,1,XSize=192,YSize=192

IDL>TVScl,image

可看到两个图像的明暗程度不同。因为这幅图像数据最大值为238,所以差别是很微弱的。

如果看不出差别,可先在0~255之间对数据进行调整:

IDL>west,0

IDL>image=Bytscl(image)

IDL>tv,image

IDL>west,1

IDL>tvscl,image

扩展:idl / idl教程 / idl程序设计

如果仍不能看到差别,可装入颜色表。Red Temperature颜色表可能起作用。键入: IDL>LoadCT,3

如果要了解TVScl作了些什么,可调整数据并用TV命令显示:

73

IDL IDL入门教程

IDL>Window,2,XSize=192,YSize=192

IDL>scaled=Bytscl(image,Top=!D.Table_Size-1)

IDL>TV,scaled

在窗口2中看到的图像应与窗口1中的图像一样。(www.loach.net.cn)这就是所说的,TVScl将数据调整为与IDL运行时所用颜色数目相适应的字节型数值。

注意:如果在显示窗口的图像不是用red_temperature颜色表显示的话,则可能是在一个16位或24位彩显上使用IDL。在这种情况下,为了下面的练习,确保关闭颜色分解器。键入如下命令:

IDL>Device,Decomposed=0

IDL>TV,scaled

如果用的是一个16位或24位显示器,为了看到新的颜色生效,在改变颜色表后,需要重新运行每个图形命令。在一个16位或24位显示器上,颜色表中的颜色没有直接被索引或连接到显示器上的色彩表。何况颜色表是图像用来查找每个像素该使用哪种颜色的一种方法。而像素的颜色是直接表示的。

一般来说,如果不知道数据是否被调整过,很可能想用TVScl命令,因为这将给图像像素值以最大可能的对比度。但是如果颜色对来说是重要的话(并且它几乎总是这样),那么可能从来不想用TVScl命令。相反,将愿意自己缩放图像数据,然后用TV命令来显示。

调整图像数据

假设正在测量大气压,并将测量数据在一色棒旁边以图像显示。可能想比较这个星期收集的图像数据和上个星期收集的图像数据。换句话说,想确定一种特定的颜色,比如红色,在这套数据中的红色和上个星期的数据中的红色表示相同的压力。

如果用TVScl命令显示这个星期和上个星期的图像数据,绝对不能保证特定的红色在两个数据组中能代表同一事情。

这些出入来自两个原因。第一,可能今天使用IDL时的颜色数目和上个星期使用IDL时不同。因为TVScl将图像数据调整到IDL运行时的颜色数目内,这可能会引起错误。第二,不能确保两组数据组间具有相同的数据范围。因而,用TVScl调整可能再次引起错误。

为解决这些问题,可用BytScl命令调整数据,并用TV命令显示。为确保IDL运行时所用的颜色数目不引起错误,可将数据调整到相同的颜色值内。并且,为确保数据集中数据的范围不引起错误,可以将数据调整到同样的数据范围。

可通过BytScl命令,应用关键字Top,Min和Max实现上述要求。例如,假设总是想以100种不同的灰度深浅或颜色深浅来显示数据,并且假设在任何数据集中希望最小数据值为15,而最大的有效值为245。可用如下BytScl命令实现:

IDL>scaledImage=BytScl(image,Min=15,Max=245,Top=99)

这个例子中, 数据调整之前在数据集中小于15的数值将设定为15。类似地,在数据调整之前,在数据集中任何大于245的数值将被设定为245。一旦数据被调整了,就可用TV命令显示。

IDL>TV,scaledImage

如果总是这样调整数据集(并且在IDL运行时总是有至少100个灰色级别或颜色数),那么上个星期的数据集就能直接与这个星期的数据集比较。一个特定的颜色,红色将总是表示一个特定的数据范围或压力。

可能在显示器上开了许多图像窗口。可用一个简单的命令删除所有开着的窗口。键入: IDL>WHILE !D.Window NE-1 DO WDelete,!D.Window

74

IDL IDL入门教程

将图像调整到颜色表的不同部位

需要知道如何调整图像数据的另一个理由,是要能在使用8位显示器时,将数据调整到颜色表的不同部位。(www.loach.net.cn]这使图像能用不同颜色显示出来,或者能将颜色表的特定色段用于特别的目的。例如,也许想将颜色表的一部分保留作为画图用的颜色。

注意:用24位彩显的一个很大的好处是能随时使用一个没有限制的颜色表。24位彩显的缺点是,在改变颜色表之后,为了看到新颜色生效,不得不重新运行图形命令(例如:TV命令)。在本书后面将看到如何编写程序,使得当一个新的颜色表装入后,能自动重新运行图形命令。

在大多数8位显示器上仅仅有一个物理颜色表,并且所有的IDL图形窗口都用它。但是通过操作颜色表可以让它看上去象是同时装入几个不同的颜色表。可以通过将不同的颜色表装入到一个物理颜色表的不同部位来实现这一点。也许实现这点的最简单的方式是在LoadCT或XLoadCT命令中用NColors和Bottom关键字。

例如,假设想用两个看上去不同的颜色表来显示同一幅图像。在用IDL打开一个图形窗口后,能通过测试系统变量!D.Table_Size的值知道在IDL运行时颜色表中有多少种颜色。如果将这个数目一分为二,就知道每个图像该用多少种颜色:

IDL>half=!D.Table_Size/2

为了在同一窗口用看上去不同的两个颜色表显示图像数据image,必须将图像数据调整为适应两个颜色空间范围的值。首先,用BytScl命令调整图像数据为适应第一个部分颜色表的值,生成一个新的图像image1:

IDL>image1=BytScl(image,Top=half-1)

现在,按如下做法将图像数据调整为适应第二个部分颜色表的值,生成第二个图像image2: IDL>image2=BytScl(image,TOP=half-1)+Byte(half)

按如下做法将两个已调整的图像肩并肩地放在同一个窗口。注意,在使用TV命令。明白这是为什么吗?

IDL>Window,XSize=192*2,YSize=192

IDL>TV,image1

IDL>TV,image2,192,0

现在需要用一个灰度颜色表(颜色表索引号为0)将左边的图像显示出来。必须将那些灰度级颜色装入颜色表中被第一个图像数据占用的部分。键入:

IDL>LoadCT,0,NColors=half,Bottom=0

如果用XLoadCT命令将颜色装入颜色表的第二部分,就能为右边的图像交互式地选择想要的任何颜色表。如下:

IDL>XLoadCT,NColors=half,Bottom=half

为了继续本章后面的例子,要恢复一个正常的颜色表,键入:

IDL>LoadCT,0

在24位显示器上用不同的颜色表显示图像

当在16位或24位显示器上运行时,使用不同的颜色表和装入颜色并显示图像一样简单。例如,如果正在一个16位或24位的显示器上运行时,可以试一试:

扩展:idl / idl教程 / idl程序设计

IDL>world=LoadData(7)

IDL>Window,1,Title=‘Gray Scale Image’

IDL>LoadCT,0

75

IDL IDL入门教程

IDL>TV,world

IDL>Window,2,Title=‘Color Image’

IDL>LoadCT,5

IDL>TV,world

显示24位图像

真彩色(或24位)图像也能用TV命令显示。(www.loach.net.cn]24位图像总是由一个3维数据集构成,它的3个维数中的一个值设为3。例如,数据集可以是一个m*n*3的数组,这种情况下,图像被认为是隔波段扫描(band-interleaved);如果图像是m*3*n则被认为是隔行扫描(row-interleaved);如果是3*m*n则被认为是隔像素扫描(pixel-interleaved)。

装载一幅24位图像,键入如下命令:

IDL>rose=LoadData(16)

这个数据组是一个按像素扫描的图像。通过键入如下命令可知道这点:

IDL>Help,rose

ROSE BYTE =Array[3,227,149]

要在一个8位显示器上显示一幅24位的图像,仅仅需要用关键字True来说明其用的是哪种扫描方式。True=1为隔像素扫描;True=2为隔行扫描;True=3为隔波段扫描。

IDL>Window,XSize=227,YSize=149

IDL>TV,rose,True=1 ;Pixel-interleaved

注意,24位图像在8位显示器上显示将表现为灰度级。要在这样的显示器上看到真彩色的图像,需要创建一幅2维图像以及伴随该24位图像或3维图像数据的红色、绿色、蓝色颜色表。这在IDL中可用命令Color_Quan来实现。如果使用8位显示器,键入如下命令:

IDL>image2d=Color_Quan(image24,1,r,g,b)

IDL>TVLCT,r,g,b

IDL>TV,image2d

现在可看到彩色图像了。

在24位显示器上显示24位图像

如果使用的是一台24位显示器,情形稍微复杂一点。为了正确显示一幅24位图像,必须打开颜色分解器。这在大多数真彩模式下的工作站上自动实现的,但在真彩模式Windows下,IDL5.2版本却不能自动实现。为确保以正确的图像颜色显示24位图像,应该在24位显示器上键入如下命令:

IDL>Device,Decomposed=1

IDL>TV,image24

注意,下载的本书配套的程序TVImage自动设置正确的颜色分解器,这取决于要显示的图像是24位还是8位。

在24位显示器上显示8位图像

在一台24位显示器上,8位图像在显示时遍历了整个颜色表。换句话说,一个8位图像的像 76

IDL IDL入门教程

素值被作为一个索引号,该索引号为给定的像素查找特定的红色,绿色和蓝色。[www.loach.net.cn)这意味着如果在使用IDL时改变了颜色表,必须重新显示该2维图像来看新的颜色是否生效。这是因为在24位显示器上颜色是在图像被显示时决定的,同时也因为正在用RGB颜色模式。并且特别要注意必须关上颜色分解器,否则将忽视颜色表矢量,并总是用灰度色彩来显示8位图像。如果用的是一个24位显示器,键入如下命令:

IDL>world=LoadData(7)

IDL>Window,XSize=360,YSize=360

IDL>LoadCT,5

IDL>Device,Decomposed=0

IDL>TV,world

为了以另一种颜色表显示图像,装入该颜色并重新运行TV命令,使图像像素值遍历整个颜色表矢量。注意当只运行LoadCT命令时,图像颜色不变。

IDL>LoadCT,3

IDL>TV,world

控制图像显示顺序

通常,当IDL显示一幅图像时,习惯上图像的第0列和第0行为图像的左下角。有些人喜欢将图像的第0列和第0行作为图像的左上角。如果喜欢第二种习惯方式,可以通过设置系统变量!Order让IDL使用习惯。在缺省时,!Order设为0。如果希望将所有图像的左上角都显示在第0列和第0行,可设置!Order=1。

如果只是希望用第二种方式显示某幅图像,可在使用TV或TVScl命令时,用关键字Order设置。例如,可以同时观看两种显示方式,键入:

IDL>Window,XSize=192*2,YSize=192

IDL>TVScl,image,Order=0

IDL>TVScl,image,Order=1,192,0

可能从别人那儿得到一个图像数据文件,显示时倒过来了。这大多是因为创建数据文件的人在排放第0列和第0行时用了不同的习惯。将关键字Order的值反过来,看是否纠正了错误.

改变图像尺寸

IDL提供了两个改变图像大小的命令:Rebin和Congrid 。

Rebin的限制为新建图像的尺寸必须是原始图像尺寸的整数倍或整数比例。例如,变量image可以在X方向或Y方向上变化为192/2和192*3个元素。但不应该是300或500个元素。图像大小也可以在一个方向减小,另一个方向增大。例如,可将变量image重新变化为384列和96行,键入如下命令。

IDL>Window,XSize=384,YSize=96

IDL>new=Rebin(image,384,96)

IDL>TVScl,new

输出图像应与图31类似。

77

IDL IDL入门教程

图31:用Rebin命令缩放的图像其大小必须与原始图像大小有整数倍关系。(www.loach.net.cn)

在缺省情况下,当放大一幅图像时Rebin采用双线性插值,当缩小一幅图像时则采用最邻近平均法。如果关键字Sample被设定后,在两个方向上都可用最邻近采样法。双线性插值更为精确,但需要更多的计算时间。

IDL>Window,XSize=192/2,YSize=192/2

IDL>new=Rebin(image,96,96,/Sample)

IDL>TVScl,new

除了下面两个方面外,Congrid与Rebin是相似的。第一,在新图像中的列数和行数可以设为任意值。第二,在缺省情况下,用的是最邻近采样法。如果想用双线性插值,必须设置关键字Interp:

IDL>Window,XSize=600,YSize=400

IDL>new=Congrid(image,600,400,/Interp)

IDL>TVScl,new

在PostScript设备上改变图像大小

象PostScript这种设备,其像素是可调节的(相对于固定像素的显示器来说),在调节图像尺寸时有所不同。(详细信息参考185页的“显示器与PostScript设备的差别”)。特别是,可不用Rebin或Congrid命令来改变图像的大小,而是用TV或TVScl命令通过关键字XSize和Ysize来改变图像的大小。

扩展:idl / idl教程 / idl程序设计

例如,当将图像输出到一个PostScript文件时,如果想将图像的显示比例定为6:4的话,也许愿意用如下代码,而不是上面的用Congrid命令将图像放大为600*400。

IDL>thisDevice=!D.Name

IDL>Set_Plot,'PS'

IDL>Device,XSize=6,YSize=4,/Inches

IDL>TVScl,image,XSize=6,YSize=4,/Inches

IDL>Set_Plot,this Device

如果图像大小和位置是用下面的归一化坐标来表示的,可以写出真正的独立于显示设备的图像显示代码。

在显示窗口中定位图像

通常显示一幅图像时,IDL将图像的左下角放在窗口的左下角。但是可通过TV或TVScl命令的附加参数来将图像移动到显示窗口中的其它位置。

例如,如果给出第二个参数,它则被视为图像在窗口中的位置。图像位置由显示窗口的尺寸和图像的尺寸计算出来的。详细算法可参阅TV命令的在线帮助。键入:

78

IDL IDL入门教程

IDL>? TV

位置可从显示器的左上角开始,一直到显示器的右下角。[www.loach.net.cn]例如,在384*384的显示窗口内,从显示器的左上角开始,对于192*192的图像来说有四种位置。键入:

IDL>Window,XSize=384,YSize=384

IDL>TVScl,image,0

IDL>TVScl,image,1

IDL>TVScl,image,2

IDL>TVScl,image,3

通过显式地指定图像左下角的象素位置来定位一幅图像是可以的。TV或TVScl命令中在图像数据名后给定两个附加参数即可以实现这点。例如,将一幅192*192名为image的图像定位于刚刚创建的显示窗口中间。可键入:

IDL>Erase,Color=!D.Table_Size-1

IDL>TVScl,image,96,96

这样,将图像的左下角放在像素点(96,96)处。当希望为附加图留下空间时,如色棒或其它的注释,这种定位图像的方法是很重要的。

例如,键入如下命令来在窗口的左边显示一个色棒,在窗口的右边显示图像。显示窗口将如图32所示。

IDL>Window,XSize=320,YSize=320

图32:此图像用颜色棒来显示其颜色梯度

IDL>ncolors=!D.Table_Size

IDL>TvLCT,255,255,0,ncolors-1

IDL>Erase,color=ncolors-1

IDL>colorbar=Replicate(1B,20)#BIndGen(256)

IDL>TV,BytScl(colorbar,Top=ncolors-2),32,36

IDL>TV,BytScl(image,Top=ncolors-2),92,64

79

IDL IDL入门教程

用归一化的坐标来定位图像

用归一化的坐标系来定位图像和确定图像大小是很方便的。(www.loach.net.cn]这与其它IDL图 象命令使用关键字Position的用法相似。(详细信息参考185页的“显示器与PostScript设备的差别”)。如果要在可变尺寸的窗口内显示图像,或者要在同一显示窗口内与其它IDL的图形程序共同使用图像,或者希望所编写的将图像传送到一个PostScript文件的IDL程序不遇到麻烦,在这些情况下,用归一化坐标定位图像和确定图像大小是非常方便的,特别是对最后一种情况。例如,刚才键入的命令是放一个色棒在图像旁。尽管这些命令在显示窗口内可以很好地工作,但如果想在PostScript设备上输出中得到类似的结果,上述命令是不可能的。(若有问题,参考185页的“显示器与PostScript设备的差别”)。

假设可以用关键字Position在窗口中确定图像的大小和位置,那么结果将如何呢?设想将图像放入一个任意的窗口内,并占满其比如80%的空间。相对于归一化坐标来说,可将图像在窗口的位置表达为:

position=[0.1,0.1,0.9,0.9]

但这是怎样被翻译成图像通常使用的设备坐标的呢?这自然要取决于显示窗口的大小。但能够知道显示窗口中可视部分的大小。这是由系统变量!D.X_VSize和!D.Y_Vsize以设备或像素单元来给定的。

通过像素坐标,可以按如下方法计算出图像所需的尺寸和在输出窗口起始的位置: xsize=(position[2]-position[0])*!D.X_VSize

ysize=(position[3]-position[1])*!D.Y_VSize

xstart= position[0]*!D.X_VSize

ystart= position[1]*!D.Y_VSize

将图像输出到显示设备和输出到PostScript文件的唯一区别是如何确定图像的尺寸。可以编写如下代码来显示图像:

IF !D.Name EQ 'PS' THEN $

TV, image, XSize=xsize, YSize=ysize, xstart, ystart $

ELSE $

TV, Congrid(image, xsize, ysize), xstart, ystart

无论是将图像输出到显示器还是输出到PostScript文件中, 上述代码都起作用。但这样做时图像的横纵比例不能得到保证。事实上,可以让图像适合窗口的形状。这对一些应用程序来说工作的很好,但对另一些却不是这样。无论在什么情况下该问题很容易解决,因为如果想保留图像的横纵比例时,只需固定好图像的一边,并以适当的方式调整图像另一边的坐标即可。

实现该项功能的代码已经写好,放在下载的程序TVImage中。无论是在显示终端,还是在PostScript文件中,TVImage都用关键字Position来定位图像和确定图像大小,。如果希望TVImage程序能完好地保持显示图像的横纵比例,可以使用关键字Keep_Aspect_Ratio。

可以用TVImage重新生成色棒位于图像左边的上述图像:

IDL>Erase,color=ncolors-1

IDL>barPosition=[32,32,52,292]/320.0

IDL>imagePosition=[92, 64, 284, 256]/320.0

IDL>colorbar=Replicate(1B, 20)#BIndGen(256)

IDL>TVImage,BytScl (colorbar,Top=ncolors-2),$

Position=barPosition

IDL>TVImage,BytScl (image,Top=ncolors-2),$

Position=imagePosition

80

IDL IDL入门教程

这样做的好处,不但因为图像可以在任何尺寸的窗口或PostScript文件中以及显示器上显示,而且因为它使得在显示窗口中轻易增加其它图形成为可能。(www.loach.net.cn)例如,可以非常容易地在色棒和图像周围放置外框或标记。键入:

IDL>TvLCT,255,255,255,ncolors-1

IDL>Plot,[0,!D.Table_Size,YRange=[0,!D.Table_Size],$

扩展:idl / idl教程 / idl程序设计

/NoData,Color=0,Position=barPosition,XTicks=1,$

/NoErase, XStyle=1, YStyle=1, XTickFormat='(A1)'?$

YTick=4

IDL>Plot, IndGen(192), IndGen(192), /NoData, $

Position=imagePosition, /NoErase, $

XStyle=1, YStyle=1, Color=0

输出结果应如图33所示。

图33:用命令TVImage不仅允许使用独立于设备的方法定位图像而且容易使用其它图形命令

从显示器中读取图像

有时花许多时间,运行许多命令,才得到了喜欢的图形显示。将图形显示读到一个图像变量中以便处理甚至硬拷贝输出是很方便的。因此,现在需要知道如何得到IDL图形窗口的拷屏。可以用TVRD命令将IDL图形窗口的内容读到一个2维IDL字节型数组中。

要在一个8位显示器上读取整个图形窗口,可键入如下命令:

IDL>Window,XSize=250,YSize=250

IDL>TVScl,image

IDL>new_image=TVRD()

IDL>Help,new_image

注意,新创建的变量现在是一个250*250字节的数组。

81

IDL IDL入门教程

在24位显示器上截屏

如果在16位或24位显示器上运行IDL,不能象上面那样使用TVRD命令。[www.loach.net.cn)16位或24位 显示器有3个颜色通道。如果象上面不用任何参数来使用TVRD命令,那么得到的2维数组的每个像素值为该像素三个通道中的最大像素值。除非装载一个灰度色彩表(这样,每个通道有同样的值),否则就不是所期望的。要想在24位显示器上截屏,只需在命令TVRD中设置关键字True即可。如果用的是16位或24位显示器则键入如下命令:

IDL>new_image=TVRD(True=1)

但是注意,得到的是一个24位图像,而不是一个2维8位图像。当显示该图像时就要用带关键字True 的TV命令:

IDL>Help,new_image

IDL>Erase

IDL>TV,new_image,True=1

读取显示图像的一部分

如果只想读显示窗口的某一部分,可指定想要的部分窗口的左下角的像素坐标和要读取的列和行的数目。换句话说,能指定矩形区域。例如,如果只想捕获上述头像的脸部,可键入:

IDL>new_image=TVRD(40,30,110,130)

在IDL中,从显示器读取的2维数组可以象处理其它图像那样进行处理。如果需要显示,可键入:

IDL>Erase

IDL>TV,new_image

IDL中基本的图像处理

IDL原是作为一种图像处理的工具,所以它有很强的图像处理能力。这节中描述的是一些IDL中基本的图像处理工具。

直方图均衡化

如果观察图像中的像素值分布,往往会发现分布趋向集中在一个狭窄的数值范围内。实际上,图像有一个非常窄的动态颜色范围。如果像素分布开,以致使像素值的每个子范围都与这些像素值一样拥有数目大约相同的像素,则该图像的信息内容就有可能增加。将像素分布到整个颜色范围的过程叫做直方图均衡化。

例如,用LoadData命令打开数据集CT Scan Thoracic Cavity。这是一幅CT扫描图像,该图像具有一个狭窄的动态颜色范围。

IDL>scan=LoadData(5)

要看变量scan的像素值分布的柱状图,键入如下命令。显示图像窗口应如图34所示。 82

IDL IDL入门教程

图34:正常图像具有狭窄的像素值分布。(www.loach.net.cn]这里的像素值集中在50-10之间。

IDL>LoadCT,0

IDL>Window,0,XSize=600,YSize=250

IDL>TV,scan

IDL>Plot,Histogram(scan),/NoErase,Max_Value=5000,$

Position=[0.5,0.15,0.95, 0.95]

可以看到大多数像素值落在50~100之间。将像素分布至整个颜色范围内,使得每种颜色值都有大致相同的像素个数,键入:

IDL>equalized=Hist_Equal(scan)

为查看新的像素分布柱状图 和 HISTOGRAM-EQUALIZED图像,键入:

IDL>Window, 1, XSize=600, YSize=250

IDL>TV, equalized

IDL>Plot, Histogram(equalized), Max_Value=5000, $

Position=[0.5, 0.15, 0.95, 0.95], /NoErase

直方图均衡化后的图像应如图35所示。

图35:一幅直方图均衡化后的图像。象素分布扩展到了整个颜色范围。

83

IDL IDL入门教程

第四章 图形显示技术

本章概要

在学会怎么显示线画图、曲面图和等值线图后,就可以用自己的想象力和创造力来显示数据了。(www.loach.net.cn)本章给出了许多特殊的可视化技术以增强数据显示能力。没有打算描述IDL中每一种可能的技术。本章将介绍一些更普遍的技术。本章的目的是为读者提供工具和概念,以便帮助读者创造自己独特的数据显示。

将学到:

1. IDL如何运用颜色

2. 怎样在IDL中创建和保存颜色谱表

3. 怎样按规范修改坐标轴的注记

4. 怎样用IDL处理坏的或残缺的数据

5. 怎样建立三维坐标系并在里面显示数据

6. 怎样组合简单图形显示

7. 怎样用动画显示图形

8. 怎样将XYZ数据格网化以便图形显示

IDL的颜色运用

IDL的颜色由三种特殊值组成。称这些数值为一个三色组,将其写成(R,G,B)即红、绿、蓝,其中红、绿、蓝代表红光、绿光、蓝光作用于该显示颜色时的数量,每个值的范围都在0到255之间。这样,一种颜色可由256级的红色,256级绿色和256级蓝色组成。这就是说IDL能显示256*256*256,或者说超过167,000,000种颜色。举例来说,黄色由亮红和亮绿组成,但没有蓝色。代表黄色的三色组写作(255,255,0)。

过去在IDL里常常用一种索引号通过查表来获得颜色三色值。现在,由于越来越多地使用24位图形卡,可直接表示三色值。如果使用索引,所查寻的这个表就被称作颜色转化表(简称为色谱表)。一个色谱表由三列数组成,一列代表红色值,一列代表绿色值,一列代表蓝色值。典型地,这些数列被称为矢量。当用IDL装载色谱表时,所做的就是选择正确的数值放进这些列或矢量之中。请看这个概念的图解(图40)。

扩展:idl / idl教程 / idl程序设计

使用索引颜色模式和RGB颜色模式

除了了解一个颜色号代表一种颜色的三色值和色谱表被用来决定三色值之外,必须意识到在IDL里有两种颜色模式。索引颜色模式用于8位显示器,RGB颜色模式用于24位显示器。(IDL在PC机和Macintosh计算机上同时使用了一种修改过的RGB颜色模式,这两种计算机支持16位颜色)。

两种模式都能用一个颜色转换表来决定用于显示的特定颜色。(当颜色分解器关闭时,RGB颜色模式就用颜色转换表。否则,RGB颜色模式就用三色值直接指定颜色)。索引颜色模式也将索引颜色号和色谱表中的特定位置联系起来,而RGB颜色模式直接指定颜色。被链接到特定色谱表某个位置的颜色被称作动态颜色显示。直接显示的颜色常被称为静态颜色显示。在大多数情况 84

IDL IDL入门教程

下(有例外),8位显示是动态显示,24位显示是静态显示。[www.loach.net.cn)

动态和静态颜色显示间最重要的区别就在于,如果用动态显示并且改变装在色谱表中特定位置的数字,索引指向那个位置的像素就会立即改变颜色。而用静态显示时,像素的颜色直接被确定,在某种程度上是永久不变的。它们不会受色谱表值中后来改变的影响。(这并不总是绝对的,阅读下面关于直接颜色视觉级的讨论。)

当这个问题好象很奇怪时,它真的很有价值。它意味着采用RGB颜色模式的系统能同时显示所有的167,000,000种颜色,而采用索引颜色模式的系统只能同时显示167,000,000色调色

板中的256种颜色。

图40:8位像素值的索引颜色模式。像素值作为索引号输入到色谱表。在色谱表中找到的红色、

绿色、蓝色值决定了与此像素值相关的或由此像素值所索引的特定三色值。

通过使用Device命令中的新关键字,可以知道正在使用的颜色模式的类型,这些新关键字是在IDL5.1中新增加的。这些关键字是Get_Visual_Depth和Get_Visual_Name。这些都是将值返回给指定IDL变量的输出关键字,键入:

IDL> Device, Get_Visual_Name=thisName, $

Get_Visual_Depth=thisDepth

IDL> Print, thisName, thisDepth

TrueColor 24

视觉名称通常为伪彩色,直接颜色或真彩色。视觉深度通常为8,16或24,它是指用于决定这个视觉级类中某个颜色的位数。

8位伪彩色视觉级表明正在使用索引颜色模式和动态颜色显示。24位真彩色或直接颜色视觉级表明正使用RGB颜色模式。直接颜色视觉级有时可能是动态颜色显示,但这是窗口管理的功能,且偶尔可由用户配置。直接色视觉级色通常用静态颜色显示。真彩色视觉级总是使用静态颜色显 85

IDL IDL入门教程

示。[www.loach.net.cn)(直到IDL5.1这个属性才保持了跨平台运行的一致性。)

静态与动态颜色视觉

伪彩色视觉级是一种动态颜色视觉。这意味着如果改变色查询表的某种颜色,显示器上任何使用该颜色索引号的像素都会立即改变颜色。一般来说,装载一种新色谱表将立即改变显示器上所有图形的颜色。

真彩色视觉级是一种静态颜色视觉。这意味着改变色谱表里的一种特定颜色决不会影响已经在显示设备上的图形或颜色,因为那些颜色是直接用RGB三色值表达的。

直接颜色视觉级最难论述。直接颜色视觉级仅用于UNIX系统的机器上。每个机器制造商对直接颜色视觉级的含义都有不同的看法。但在理论上直接颜色视觉级应该集两者的优点于一身:即24位颜色系统表现得仿佛是一个动态颜色视觉。在实践中,很少看到它运行得很好。最普遍的问题是直接颜色视觉级通常提供私有的颜色图,要求将图形窗口作为当前窗口,并在窗口中装载正确的颜色。当完成时,其它窗口会消失。这就是常见的“颜色闪烁问题”,它是X窗口管理器处理色谱表的方式造成的结果。近来硬软件的发展已消除了许多这样的问题,但它们仍常常会被碰到。通常,可用8位伪彩色视觉级或24位真彩色视觉级,因为这样能保证在多个平台上正确地工作。

当IDL启动时,其所用的视觉级正常情况下是按缺省值给定的,即可以从.Xdefaults文件中获取信息(当IDL运行在UNIX机器上),也可以按IDL的一般规则来给定视觉级。(这种规则要求IDL查询硬件支持何种视觉级,并指定可获得的“最高”视觉级和视觉深度)。这个指定的缺省值可在IDL中指定视觉级和视觉深度来替换。(IDL的微机版本是通过装在机器上的图形卡及其配置来给定这些参数的,这不能从IDL内部改变。)指定值必须在图形窗口打开前确定,并可作用于IDL运行期间。

下面是基于UNIX系统机器上的典型的视觉级赋值语句:

IDL> Device, PsuedoColor=8

IDL> Device, TrueColor=24

在8位显示器上指定颜色

如果正在使用索引颜色模式,可指定一种特定的颜色做为索引号进入色谱表。IDL在表里寻找此颜色索引号,并在色谱表内找出红、绿、蓝色列中的值作为确定该颜色的三色值。例如,假设将代表黄色的三色值(255,255,0)装载到色谱表的第10项。可用TvLCT命令来实现:

IDL>TvLCT, 255, 255, 0, 10

如果想用黄色画图,可以用Color关键字指定颜色索引号,如下:

IDL> data = LoadData(1)

IDL> Plot, data, Color=10

类似地,任何值(即索引)为10的图像像素都将用同样的黄颜色显示。

如果正在使用索引颜色模式,可以简单地装载新的三色值进入色谱表的第10入口,就可容易地更改图形颜色。例如,可以像这样装载绿色:

IDL> TvLCT, 0, 255, 0, 10

显示图形的颜色立即改变了,因为此颜色已被索引到色谱表。

86

IDL IDL入门教程

在24位显示器上指定分解后的颜色

如果用24位显示器,情况稍微复杂一些。[www.loach.net.cn)当IDL用RGB颜色模式时,在缺省值情况下,采用“分解后的”颜色。这就是说IDL不是将颜色索引作为单独的索引号传入色谱表,而是试图将索引分解为三个单独的索引号传入色谱表。它这样处理是假设该索引为一个24位长整型数。IDL使用最低的8位数作为红色索引,中间的8位作为绿色索引,最高的8位作为蓝色索引。那么上面命令里的数字10被看做在红色矢量的第10项,但在绿色矢量和蓝色矢量中却是第0项。将在图41里看到分解索引值的图解。

扩展:idl / idl教程 / idl程序设计

图41:RGB颜色模式用24位像素值来单独指定一种颜色的RGB分量。如果颜色矢量包括从0

到255的值,所有的16,700,000种颜色都能在调色板里同时获得。

当灰色度色谱表被装载时(如图41所示),所有的16,700,000种颜色可立即被IDL存取。例如,想在24位系统上用黄色画图,可选择24位整数,其中8个最低位设为1(全红),8个中间位设为1(全绿),8个最高位设为0(无蓝色)。这时,代表黄色的数字用长整数65535表示。为了在24位颜色显示设备上画出上述图形,可键入:

IDL> Plot, data, Color=65535L

因为大多数人都不能熟练地使用24位的字节数,此数字有时被表示成十六进制符号。那么用两个数字(0-F)就足以一次设置8位(也就是,256或2^8能由两个十六进制数设置)。例如用十六进制符号表达全红和绿, 但没有(不能表达)蓝:

IDL> Plot, data, Color=’00FFFF’xL

为了在碳灰色(70,70,70)背景上,画出含有绿色(0,255,0)标题的黄色(255,255,0)图形,可使用以下十六进制符号: IDL> Plot, data, Color=’00FFFF’xL, Background=’464646’xL

87

IDL IDL入门教程

IDL> XYOutS, 0.5, 0.95, Align=0.5, /Normal, ‘Plot Title’, $

Color=’00FF00’xL

如果对24位符号或十六进制符号都不熟悉,可能会想用程序Color24来获得24位整数值。(www.loach.net.cn)这个程序在已经下载的本书配套程序中,可以将任何RGB三色值(写成三个元素的IDL矢量)转换成等值的24位整数值。例如,在24位系统上,如果想用程序Color24来画黄色图形,命令如下:

IDL> Plot, data, Color=Color24([255,255,0])

如果想写出能运行在8位或24位显示设备上的代码,代码也许类似如下:

Device, Get_Visual_Depth=thisDepth

IF thisDepth GT 256 THEN BEGIN

Plot, data, Color=Color24([255,255,0])

ENDIF ELSE BEGIN

TvLCT, 255, 255, 0, 100

Plot, data, Color=100

ENDELSE

在24位显示设备上指定没有分解过的颜色

不必用分解过的颜色通过RGB颜色模式作索引。例如,可能希望以8位显示设备上装载色谱表的方式装载色谱表,并能在8位显示设备和24位显示设备上使用同样的代码。如果关闭颜色分解功能,这是可能实现的。可通过这样的设备命令完成:

IDL> Device, Decomposed=0

在这种情况下,IDL是以处理8位颜色索引那样的方式处理像素值或颜色索引。也就是说,索引被用来存取在红、绿、蓝色谱表矢量里相同的项。这样,在24位显示设备上,用没有分解的颜色,也可用下面的命令画出黄色图形:

IDL> TvLCT, 255, 255, 0,10

IDL> Plot, data, color=10

但图形的颜色是直接表达的,这一点极端重要。它不被索引到色谱表里的入口位置,如果在第10入口处改变颜色表的值,图形颜色将完全不受影响。

IDL> TvLCT, 0, 255, 0, 10

将不得不重新用线形命令(或者,通常重新显示图形)来看新颜色生效。

IDL> Plot, data, color=10

在Windows系统中,无论颜色分解怎么设置,IDL5.1以前的版本总是在24位显示设备上显示8位图像,就象正在使用没有分解过的颜色。也就是说,8位像素值被用来索引色谱表中全部三种矢量的相同项。这种特点(PC版本里的一种长期BUG)在IDL5.1里得到改变,从而使得这种特性和其它平台保持了一致性。然而,这种改变使得写出在8位和24位环境中同样工作的8位图像显示程序时稍微困难一点。

决定颜色分解的开与关

自从IDL5.1.1开始,没法保证在24位显示设备里的颜色分解是开还是关。这意味着如果想让分解颜色是关或者是开(例如显示8位图像时想要关闭颜色分解,在显示24位图像时想打开颜色分解),必须在调用图形显示命令前设置它:

88

IDL IDL入门教程

Device, Decomposed=0

TV, image8bit

Device, Decomposed=1

TV, image24bit

一个新的Get_Decomposed关键字被引进IDL5.2的Device命令,它能告诉目前的“分解”状态。[www.loach.net.cn)

IDL> Device, Get_Decomposed=usingDecomposed

IDL> Print, usingDecomposed

注意,关于24位颜色方面,微机版的IDL5.2仍有BUG出现。如果正在使用24位显示设备,且想用正确的图像颜色显示24位图像,必须装入色谱表0或将Decomposed关键字设为1。如果将Decomposed值设为0,那么即使是24位的图像颜色也将遍历色谱表矢量。注意,通过下载的本书附带程序TvImage可以根据图像是8位(Decomposed=0)或24位(Decomposed=1)来正确地设置Decomposed值。

在24位显示设备上装载色谱表

现在知道了当在8位显示设备上使用索引颜色模式时像素颜色被直接索引到色谱表。换句话说,如果通过在IDL里装载色谱表来改变色谱表里的值,那么与那些索引联系在一起的颜色也将被改变。如果想同时分别用不同的色谱表显示几个图像,必须将可获得的色谱表索引分成不同的区,每个区装载不同的颜色(参看67页的“将图像调整到颜色表的不同部位”的有关章节)。从实际来说,在用完索引数字前,可能至多有四或五幅具有不同色谱表的图像。

24位显示设备的突出优点之一就是可以一次在显示设备上实际显示很多的图像,每一个图像都可用不同的色谱表。(记住只有关闭颜色分解后,在24位显示设备上装载色谱表才有意义。记住当IDL启动时,它的缺省值是打开的。)

如果装载不同的色谱表会怎样呢?想改变很多图像的颜色吗?可能不,因为每幅图像都是用它自己一套颜色,它们都是直接指定的。

如果有一幅图像显示在24位监视器上,用LoadCT命令或XloadCT工具改变色谱表,那么新的颜色不会生效,除非重新显示图像,并将图像像素通过色谱表重新转换成特定的直接颜色。想了解如何写代码以改变24位显示设备上的色谱表以及让图形自动重新显示,请阅读274页 “在24位显示设备上改变色谱表”的有关章节。

获得色谱表的拷贝

有两种方法取得当前色谱表中红、绿、蓝值的拷贝。一个方法是声明公共块Colors,既可在想获得色谱表的IDL主程序中声明,也可在任何IDL程序或函数里声明。调用过程如下: COMMON Colors, r_orig, g_orig, b_orig, r_cur, g_cur, b_cur

扩展:idl / idl教程 / idl程序设计

注意,色谱表必须在IDL的运行时装载,以便定义公共块中的变量。

约定从前面的三个变量中获得当前色谱表的颜色。如果想修改这些颜色,可将修改后的颜色矢量放进最后三个变量。装载色谱表用TvLCT命令。如果想反转色谱表里的颜色,可键入:

IDL> COMMON Colors, r, g, b, rr, gg, bb

89

IDL IDL入门教程

IDL> rr = Reverse (r)

IDL> gg = Reverse (g)

IDL> bb = Reverse (b)

IDL> TvLCT, rr,gg, bb

另一个获得色谱表值的方法是用带Get关键字的TvLCT命令:

IDL> TvLCT, red, green, blue, /Get

在这个例子中,变量red, green和blue为输出变量,被赋予了色谱表中相应的值。(www.loach.net.cn]注意这些变量拥有与正在使用的IDL的颜色数目一样多的元素。通过第一个打开的IDL图形窗口,可以确定正在使用的颜色数,键入:

IDL> Print, !D.Table_Size

注意,Windows版的IDL在24位显示设备里面,这个数字可能不精确,因为它将取决于颜色分解是开还是关。详细信息参考89页的“决定颜色分解的开与关”。

修改和创建色谱表

使用色谱表颜色有两个基本命令:XloadCT和Xpalette。通过这两个命令,应能使色谱表颜色调整为所需要的。XloadCT允许用不同的方法扩展颜色。(进入Function模式并点击Add Control Point按钮几次。用鼠标移动控制点来看如何影响色谱表。)它也允许以交互方式进行Gamma矫正。(一的Gamma值是一个线性斜坡函数。小于或大于一的Gamma值是不同形状和陡峭的指数斜坡函数。)

Xpalette命令允许通过设置滚动条的端点色和插入干涉值来修改和创造自己的色谱表。单个颜色也可在这个程序里被修改。注意是在颜色系统而非RGB颜色系统里用Xpalette指定颜色。然而最后,无论是用什么颜色系统指定颜色,被装载进色谱表中的是红、绿、蓝色矢量。

创建自己的色谱表也很容易。下面就是一个能创造两种端点色之间的任意颜色数的色谱表,名为Make_CT的简单小程序(不带错误检查!)打开文本编辑器并键入:

FUNCTION MAKE_CT, begColor, endColor, ncolors

ScaleFactor = FindGen(ncolors) / (ncolors – 1)

Colors = BytArr(ncolors, 3)

FOR j=0,2 DO colors[*,j] = begColor[j] + (endColor [j] $

- begColor [j]) * scaleFactor

RETURN, colors

END

编辑此程序须键入:

IDL> .Compile make_ct

打开World Elevation Data图像,并显示在窗口中:

IDL> image = LoadData(7)

IDL> Window, Xsize=360, Ysize=360

IDL> LoadCT, 0

IDL> TVScl, image

假如想要从黄色(255,255,0)到蓝色(0,0,255)的色谱表。可以用程序Make.CT创造并如下装载:

IDL> yellow = [255, 255, 0]

IDL> blue = [0, 0, 255]

IDL> TvLCT, Make_CT(yellow, blue, !D.Table_Size)

90

IDL IDL入门教程

假如想用从黄到绿到蓝的150种颜色来显示此图。(www.loach.net.cn)可以这样做:

IDL> scaledImage = BytScl(image, Top=149)

IDL> green = [0, 255, 0]

IDL> TvLCT, Make_CT(yellow, green, 75)

IDL> TVLCT, Make_CT(green, blue, 75), 75

IDL> TV, scaledImage

注意在选择颜色的过程中可能产生很多混乱数据。要当心。可能想看看Bernice E. Rogowitz和Lloyd A. Treinish在Computers In Physics, 10(3):268,1996上所著的名为 “How Not to Lie with Visualization”的论文。如果真对颜色和数据显示感兴趣,读阅Edward Tufte的The Visual Display of Quantitative Information and Envisioning Information.这些书正好可能改变读者编写IDL程序的方法!

保存自己的色谱表

假如对刚刚创建的色谱表很满意并想保存它。可以用TvLCT命令得到颜色值:

IDL> TvLCT, r, g, b, /Get

查看矢量有多长可键入:

IDL> Help, r, g, b

可以看到它们和正在IDL里应用的颜色数目一样长。但上述色谱表的颜色只是在前150个值里。可用颜色值的数目重新限定矢量:

IDL> r = r(0:149)

IDL> g =g(0:149)

IDL> b = b(0:149)

现在如果愿意可以保存矢量,但大多数色谱表矢量在长度上有256个元素。很容易使这些矢量达到该长度:

IDL> r = Congrid(r, 256, /Interp)

IDL> g = Congrid(g, 256, /Interp)

IDL> b = Congrid(b, 256, /Interp)

如果愿意,可以将这些矢量写进文件,但用IDL的保存命令将它们保存在IDL的保存文件中则更容易:

IDL> Save, File=’mycolors.sav’, r, g, b

为了证明是否保存了这些矢量,可装载另一个色谱表并消除这三个变量:

IDL> LoadCT, 0

IDL> DelVar, r, g, b

当准备用这些矢量时,用Restore命令重新恢复它们。注意它们以保存时同样的变量名返回。如果在IDL定义了的同名变量(象在此处做的),此变量将被覆盖。这意味着需要给出一个仔细琢磨过的输出变量名:

IDL> Restore, ‘mycolors.sav’

IDL> Help, r, g, b

使用这些变量时,必须将这些变量重置为IDL运行时的颜色数目。命令如下:

IDL> r = Congrid(r, !D.Table_Size)

IDL> g = Congrid(g, !D.Table_Size)

IDL> b = Congrid(b, !D.Table_Size)

IDL> TvLCT, r, g, b

91

IDL IDL入门教程

与其每次想调入所保存的一个色谱表时都要键入这些命令,不如写个小程序来自动调用更容易。[www.loach.net.cn)如果总是用变量名r, g和b保存RGB矢量,可以编写名为CT_Load的程序。(在这个小程序里没有错误检查!)打开文本编辑器并键入:

PRO CT_Load, filename

IF N_Params( ) EQ 0 THEN filename = ‘mycolors.sav’

Restore, filename

r = Congrid(r, !D.Table_Size)

g = Congrid(g, !D.Table_Size)

b = Congrid(b, !D.Table_Size)

扩展:idl / idl教程 / idl程序设计

TvLCT, r, g, b

END

保存ct_load.pro文件并编译它:

IDL>.Compile ct_load

现在无论什么时候想调用这个色谱表,只须键入:

IDL> CT_Load

创建自己的轴标注

IDL缺省的基本轴标注将不足以满足显示要求。幸运地是IDL提供了许多方法来增加轴的基本注释特性。本节将讲述一些创造更复杂的轴标注的一些技巧。

调整轴刻度间隔

有时IDL内部的轴标注算法不会用最有利于数据方式来划分的。可以用[XYZ]Ticks关键字来控制主刻度间隔的数目。装入随本书附带的数据集Time Series Data。键入以下命令:

IDL> curve = LoadData(1)

IDL> LoadCT, 5

IDL> Plot, curve

注意X轴被划分为五个主刻度间隔。可以修改为十个主刻度间隔,键入:

IDL> Plot, curve, Xticks=10

输出结果见图42.

注意小刻度也随之增加导致轴上刻度有点凌乱。既然对这样精细间隔的轴刻度不感兴趣,可以改变小刻度的数目。可用[XYZ]Minor关键字设置小刻度的数目。例如,可以只在两个主刻度之间设一个小刻度。可能想将Xminor关键字设为1,以便得到每个主间隔之间有一个小刻度,但这是不正确的。如果Xminor关键字设为1,所有的小刻度都会消失。为了得到想要的小刻度的数目,须将Xminor关键字设为比想要的数目大1。键入:

IDL> Plot, curve, Xticks=10, Xminor=2

92

IDL IDL入门教程

图42:主刻度间隔的数目随Xticks关键字改变

格式化轴的标注

影响轴标注的另一个方法是改变轴的标注格式。(www.loach.net.cn)例如,X轴标现在被表示为整数。可能想用三位数的整数来表示。可以通过将XtickFormat关键字设定为想要的特定格式来达到这个目的。例如,可以键入:

IDL> Plot, curve, XtickFormat=’(I3.3)’

用小数点后面带两位小数的浮点值来写标注,键入:

IDL> Plot, curve, XtickFormat=’(F6.2)’

也可以用特殊字符串作为刻度。这可以用TickName关键字完成,最多可达30个字符串元素。例如,可以用星期几标识图形:

IDL> labels = [‘MON’,’TUE’,’WED’,’THU’,’FRI’,’SAT’]

IDL> Plot, curve, XTickName=labels

输出结果如图43所示。

通过将轴刻度格式设置为(A1),可隐藏轴标注:

IDL> Plot, curve, XtickFormat=’(A1)’

编写刻度格式函数

另一个格式化刻度的方法是编写一个函数,用所想要的格式来格式化刻度。如果传给

[XYZ]TickFormat关键字的参数是函数名,那么IDL将注释标注时将调用那个函数。

93

IDL IDL入门教程

例如,假设在这个图形上想要的X轴标是日期,写作25 MAR 97。[www.loach.net.cn]可以编写名为Date的函数来完成格式化。这个函数必须定义三个且只能有三个定位参数。它们是轴参数,索引数,和标注值。当需要格式化轴标注时,IDL会用这三个定位参数来调用该函数。函数的返回值必须是字符串变量。

图43:可以通过[XYZ]TickNames关键字用字符串标识轴。

轴参数为0,1或2分别表示X、Y或Z轴。索引数是特定轴标的个数。这个参数程序员很少在函数里用到。标注值是用于轴标的常规值。工作就是用标注值来计算或格式化该函数返回的新值。就是这个返回值用来为特定轴索引数标注轴的。

通过一个例子可更容易了解如何编写这个程序。打开文本编辑器键入这个简短的Date程序。

FUNCTION DATE, axis, index, value

MonthStr = [‘Jan’, ’Feb’, ’Mar’, ’Apr’, ’May’, ’Jan’, ’Jul’, $

‘Aug’, ‘Sep’, ‘Oct’, ‘Nov’, ‘Dec’]

CalDat, LONG(value), month, day, year

Year = StrMid(StrTrim(year,2), 2, 2)

RETURN, StrTrim(day, 2) + ‘ ‘ + monthStr (month-1) + ‘ ‘ $

+ year

END

编译程序Date,以便以下的代码用来格式化X轴刻度标识:

IDL> .Compile date

注意这个程序的CalDat命令。此程序接受代表某个日期的Julian数值,并返回与此Julian数值相关的正确的日,月,年数。这个信息可被用来正确地格式化标识。为了解它是如何工作的,可键入:

IDL> Window, XSize=500, YSize=350

IDL> startDate = Julday (1, 1, 1991)

94

IDL IDL入门教程

IDL> endDate = Julday(6, 23, 1995)

IDL> numTicks = 5

IDL> sizeCurve = N_Elements(curve)

IDL> steps = Findgen(sizeCurve) / (sizeCurve-1)

IDL> dates = startDate+ (endDate+1 – startDate) * steps

IDL> !P.Charsize = 0.8

IDL> Plot, dates, curve, XtickFormat=’Date’, $

Xstyle=1, Xticks=numTicks, $

Position= [0.15, 0.15, 0.85, 0.95]

输出结果见图44。(www.loach.net.cn)若想更多地了解用日期标识轴的情况,可参考IDL库函数Label_Date。此函数功能很象刚刚编写的程序Date的功能。

图44:通过用户编写的函数格式化刻度。

正如所见,这些标识很长,且有挤在一起的危险。可能想用另外的方法显示日期。例如,可能想将它们相对轴旋转45度。不幸的是,刚刚编写的刻度格式化函数不能奏效,将不得不借助更有效的方法,用XYOutS命令放置标识。

然而,仍可以用程序Date来格式化字符串。为完成这项工作,必须画一幅X轴不带标注的图,并需要一个有正确刻度值的矢量。可以通过将轴的刻度格式设为(A1)来隐藏轴标注。可用Xtick_Get关键字以矢量的形式得到刻度值。例如图形可以这样画(Position关键字用来给轴标留下空间):

IDL> Plot, dates, curve, XtickFormat=’(A1)’, Xstyle=1, $

Xticks=numTicks, Xtick_Get=tickValues, $

Position= [0.1, 0.2, 0.85, 0.95]

扩展:idl / idl教程 / idl程序设计

然后,用XYOutS命令将标注添上。可以用![XY].Window系统变量来找出X和Y轴端点的归一化坐标。这个信息对于正确定位标注是非常关键的。代码如下:

IDL> ypos = Replicate (!Y

.Window[0] – 0.04, numticks+1)

95

IDL IDL入门教程

IDL> xpos = !X.Window[0] + (!X.Window(1) - !X.Window[0] )* $

FindGen (numTicks+1) /numTicks

IDL> FOR j=0,numTicks DO XYOutS, xpos(j), ypos(j), $

Date (0, j, tickValues [j]), Alignment=0.0, $

Orientation= - 45, /Normal

输出结果如图45所示。(www.loach.net.cn]

图45:用XYOutS命令创建旋转的轴标。

用IDL处理残缺的数据

不幸的是,数据并不总是来源于性能良好的采集仪器。将原始数据处理为可用的形式通常是必要的。事实上,很多原始数据组是不完整的。许多事情都可能发生。例如,数据采集仪器关闭一小段时间;电流导致伪数据;操作者错误地操作;等等。如何用IDL来处理这种残缺的或坏的数据呢?

处理这种数据的一种方法是赋给它一个NaN值。NaN只是一种特殊的位模式,在每种机器结构上都是不同的。运行IDL的机器的位模式保存在系统变量!Value的F_NaN字段中。 看看它如何被使用,可用LoadData命令打开Elevation Data数据组:

IDL> data = LoadData (2)

IDL> !P.CharSize = 1.0

这个数据组是41*41的浮点数组。假设数据是不完整的。假设在采集数据中,在扫描此二维数据组的中间三行时,采集仪器暂时关闭。想将NaN值赋给这三行扫描数据,可键入:

IDL> badData = data

IDL> badData (*, 30:32) = !Values.F_NaN

现在,当显示曲面图时,IDL不将那些是NaN的值联接起来,键入:

IDL> Surface, badData

96

IDL IDL入门教程

输出结果如图46所示。(www.loach.net.cn)

图46:在此曲面图里,残缺的数据用NaN代替。

除了设置NaN位模式外,还有另一个方法可以处理残缺的或坏的数据。这就是用大多数IDL图形输出命令都有的关键字Min_Value和Max_Value。通过设置这些关键字,任何小于最小值或大于最大值的都可以被图形输出命令忽略。例如,在以上所用到的高程数据集里,可以只画特定范围里的那些等值线。以下是一些显示它如何工作的命令。这儿值在小于等于400和大于等于1000之间的等值线没有在图中画出:

IDL> Window, XSize=500, YSize=375

IDL> !P.Multi = [0, 2, 1, 0, 1]

IDL> Values = FindGen (10)*150 + 100

IDL> label = Replicate (1,10)

IDL> Contour, data, Levels=values, /Follow, C_Labels=label

IDL> Contour, data, levels=values, /Follow, C_Labels=label, $

Min_Value=400, Max_Value=1000

IDL> !P.Multi = 0

图形窗口的右边部分输出结果如图47所示。

97

IDL IDL入门教程

图47:等值线可用Contour命令的Min_Value和Max_Value关键字消除。[www.loach.net.cn)

用IDL建立三维坐标系

IDL用变换矩阵乘上三维空间的每个点,从而在二维显示上模拟三维坐标系。如果有这种变换矩阵,那么它将会存储在系统变量!P.T里。如果想用IDL在三维空间里画图,必须首先将正确的变换矩阵装入!P.T系统变量。接着必须确保在图形输出到显示设备之前,图形输出命令已经被该矩阵乘过。在实践中这是很容易做到的。

将三维变换矩阵装入!P.T系统变量有几个方法。如果想严格控制矩阵的建立,可以用T3D命令来建立想要的三维坐标系。但除非正在做某件复杂的或超出常规的事情,否则不会愿意使用T3D命令。可用以下两个方法之一来装载三维变换矩阵:(1)如果想在三维空间中显示坐标轴,可用带Save关键字的Surface命令;(2)如果仅仅希望创建三维空间,而不关心坐标轴的显示问题,可以用Scale3命令。

建立三维散点图

假设有随机分布的三维数据,并想将它在三维空间里显示为散点图。也可以通过计算机获得随机分布的三维数据,键入:

IDL> seed = 3L

IDL> x = RandomU (seed, 41)

IDL> y = RandomU (seed, 41)

IDL> z = Exp (- 4 * ((x – 0.5)^2 + (y – 0.5)^2))

看看随机分布的数据,键入:

IDL> Window, XSize=400, YSize=350

98

IDL IDL入门教程

IDL> plot, x, y, pSym=4, SymSize=2.0

在这种情况下,需要有一套坐标轴来定义三维空间。(www.loach.net.cn]对于建立三维变换矩阵来说,用带Save关键字的Surface命令将是一个好的选择。Save关键字将为Surface命令创造的三维变换矩阵保存在!P.T系统变量里,而不是丢掉。可以用常用的轴旋转关键字来取得想要的三维空间。NoData关键字只显示坐标轴。建立三维空间所必需的[XYZ]Range关键字反映了正确的真实数据范围,而不是Surface命令中的伪值范围。键入:

IDL> Surface, Dist (10), /Save, /NoData, CharSize=1.5, $

XRange=[0,1], YRange=[0,1], ZRange=[0,1]

上述命令建立了常规的三个轴。也可以增添附加轴。例如,需要突出XY平面。可以用Axis命令增添附加的X和Y轴:

IDL> Axis, YAxis=1, 1.0, 0.0, 0.0, /T3D, CharSize=1.5

IDL> Axis, Xaxis=1, 0.0, 1.0, 0.0, /T3D, CharSize=1.5

为了在这个三维空间内画点,每个三维点都必须和变换矩阵相乘。可以通过在图形输出命令中设置T3D关键字来实现此项功能。在这种情况下,可以用PlotS命令:

IDL> Plots, x, y, z, Psym=4, SymSize=2.0, /T3D

扩展:idl / idl教程 / idl程序设计

为了使图形的立体感更强,可以使用一条线将每个点连接到XY平面上。事实上,