4. 1.2. 自定义应用程序属性
问题
我要改变SWF的尺寸或背景颜色
解决方法
指定项目属性里的编译器参数或者class文件的 metadata 。
讨论
不像早期版本的Flash,ActionScript 3.0 编译器真正是一个命令行编译器。你可以通过命令行再
加上一长串参数来创建类或目录,在eclipse里让这一切变得更简单些。
当建立ActionScript 工程后,默认情况下会生成500x375 尺寸的 .swf, 帧速为24/秒,背景色为
蓝色。我们可以改变这些设定,有几种方法。
第一种方法就是通过ActionScript编译器参数改变编译器设定。右键点击工程,在菜单中选择
Properties ,然后在左边选择ActionScript Compiler,在右边找到"Additional compiler arguments."
在这里就可以输入参数了,下面是些常见的参数
23
-default-size width height
01
ye
-default-background-color color
-default-frame-rate fps in
ix
可以这样写:
/l
et
-default-size 800 600
.n
-default-background-color 0xffffff
dn
cs
-default-frame-rate 31
/b !
g.
第一个参数设定输出swf尺寸为800x600 象素. 第二个参数设定背景色为白色, 第三个参数设定
:/ 译
lo
播放帧速为31帧每秒。多个参数可以这样写:
tp 翻
ht 青
-default-size 800 600 -default-frame-rate 31
常
第二种方法就是通过类文件种的metadata 来改变设定。Metadata 中包含的语句不会立即被解
释,但是在编译的时候编译会去检测。下面的语句具有等同效果
[SWF(width="800", height="600", backgroundColor="#ffffff", frameRate="31")]
这一行语句放在import之后,类定义之前,如:
package ...{
import flash.display.Sprite;
[SWF(width="800", height="600", backgroundColor="#ffffff", frameRate="31")]
public class ExampleApplication extends Sprite
{
public function ExampleApplication()
{
}
}
}
5. 1.3. 在哪里写ActionScript 代码呢
ActionScript
问题
当你有了ActionScript工程后,接着就需要知道任何输入代码。
解决方法
在类结构中或方法体中添加 ActionScript 代码
讨论
在 以前的ActionScript 1.0 和 2.0中, 有多种途径添加代码:在时间线上,按钮上或电影剪辑上,
在电影剪辑的时间线上通过#include命令引入外部的as文件或class文件。但是 ActionScript 3.0
是完全基于类的,所以所有的代码都必须放置在类文件中。
当你创建一个新的 ActionScript 工程后,主类文件被自动创建,并且在代码视图中代开了,刚
开始的代码大概是这样的:
23
package ...{
01
ye
import flash.display.Sprite;
in
ix
/l
public class ExampleApplication extends Sprite
et
...{
.n
dn
public function ExampleApplication( )
cs
...{
/b !
g.
:/ 译
lo
tp 翻
}
ht 青
}
常
}
可能你很熟悉 ActionScript 2.0中的类, 但是3.0发生了很多变化,这些我们将在第二章讨论,在
这里先学完基础概念先。
首先注意到代码顶层有个关键字 package ,Packages 包 ) 是用来组织一群相关联的类文件的 。
(
在 ActionScript 2.0, 包是用来判断类文件的路径。在 ActionScript 3.0 中必须指定包,例如,我
们有个utility类包,要这样申明:
package com.as3cb.utils {}
如果你不指明包名,那么该类就输入最顶层的默认包。
接下来,加入 import 语句,引入一个类就相当于在当前的代码文件中创建了使用该类的快捷
方式,这样我们就不需要输入全路径来使用它了。例如,你可以使用下面的 import 语句:
import com.as3cb.utils.StringUtils;
6. 这样我们就可以直接引用 StringUtils 这个类了。从 flash.display 引入Sprite 类是因为默认的
类文件继承了Sprite 类。
接下来就看到我们的主类 ExampleApplication,注意到在class关键字前有个关键字 public ,
表明该类是共有的。最后有个公共方法,方法名和主类一样,这种方法称为构造器,当一个类
实例被创建时,其构造器会被自动执行,在这里,当swf文件被 Flash 播放器载入时构造器就
会被执行。
package ...{
import flash.display.Sprite;
public class ExampleApplication extends Sprite ...{
public function ExampleApplication( ) ...{
graphics.lineStyle(1, 0, 1);
for(var i:int=0;i<100;i++) ...{
graphics.lineTo(Math.random( ) * 400, Math.random( ) * 400);
}
}
23
}
01
ye
}
保存然后运行程序,浏览器会打开一个html文件,显示一个swf里画了100条随即直线。正如你 in
ix
所看到的,当swf被播放器载入后构造器就会被执行。
/l
et
在这里联系中,我们把代码直接写在了构造器中,但时最好的办法是在构造器中引用一个方法,
.n
dn
在这个方法中添加代码.
cs
对于新手来说,现在你已经学会了如何添加代码了。
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
1.4. 如何跟踪信息
问题
你需要在运行时跟踪信息或某个数据变量
解决办法
使用trace函数,把数据传给它,运行程序,你会发现信息已经在Eclipse的控制台下输出了。
讨论
你可以跟踪一个消息或一个变量的值,也可以输出任何其他数据,就像你在早期的版本中那样,
比如:
trace("Hello, world");
trace(userName);
7. trace("My name is " + userName + ".");
一旦swf在外部浏览器里运行,就没办法捕获 trace输出的信息了,幸运的是 Flex Builder2有
Console视图,Console视图就相当于Flash的Ouput面板。
需要注意的是使用trace则必须在调试模式下运行程序,这样才能在Console视图显示数据,下面
的代码创建一个变量,然后赋值,然后用trace输出。
package {
import flash.display.Sprite;
public class ExampleApplication extends Sprite {
public function ExampleApplication( ) {
var userName:String = "Bill Smith";
trace("My name is " + userName + ".");
}
}
23
01
}
ye
现在在调试模式下运行程序,运行完关闭浏览器,你会看到在Eclipse下已经输出数据了。 in
ix
/l
要运行debug版本的程序,必须要安装debug版本的Flash播放器。否则会显示错误信息,另外debug
et
版本的播放器可以把输出信息到一个文件上,查找mm.cfg文件。一般信息如下:
.n
dn
cs
/b !
g.
Operating system Location
:/ 译
lo
Windows XP C:Documents and Settings[user name]mm.cfg
tp 翻
ht 青
Windows 2000 C:mm.cfg
常
Mac OS X MacHD:Library:Application Support:macromedia:mm.cfg
mm.cfg 文件允许你设置如下变量:
TraceOutputFileEnable
设置值为 0 (不写入文件) 或 1 (写入文件).
TraceOutputFileName
文件路径,如果没有指定,会在mm.cfg的同目录下生成一个叫flashlog.txt文件
ErrorReportingEnable
设置值为 0 (不输出错误信息到文件) 或 1 (输出错误信息). 默认为0
MaxWarnings
写入文件的错误信息数量。如果为0则没有限制。
例子:
8. TraceOutputFileEnable=1
TraceOutputFileName=C:flex.log
1.5. 处理事件
问题
我要重复执行某段代码
解决办法
在enterFrame事件中添加监听器和关联处理方法
23
讨论
01
ye
在 ActionScript 2.0 中 处 理 enterFrame 事 件 是 很 简 单 的 , 你 只 要 创 建 时 间 线 函 数 调 用
onEnterFrame 然后每次新帧开始时就会自动调用。在 ActionScript 3.0 中有各种各样的事件需 in
ix
要控制,访问他们也是不难的。
/l
et
如果你熟悉ActionScript 2.0中的EventDispatcher 类的话,你就很好理解 ActionScript 3.0 事件句
.n
柄了。要广播 enterFrame 事件,你要告诉你的程序去监听这个事件然后指定回调函数。用
dn
cs
addEventListener 方法可以做到:
/b !
g.
:/ 译
lo
addEventListener(type:String, listener:Function)
tp 翻
ht 青
type 参数指出你要监听的事件类型,比如"enterFrame". 然而自己输入这些字符串容易出错,最
常
好的办法就是调用 Event 类的静态成员属性:导入Event 类,调用addEventListener 方法:
addEventListener(Event.ENTER_FRAME, onEnterFrame);
第二个参数onEnterFrame,指向类中定义的回调函数,该函数需要传递进EVENT的一个实例:
import flash.events.Event;
private function onEnterFrame(event:Event) { }
event 对象包含一些于该事件有关的信息。这里有个简单的例子:画出一些随机线。
package {
import flash.display.Sprite;
import flash.events.Event;
public class ExampleApplication extends Sprite {
public function ExampleApplication( ) {
graphics.lineStyle(1, 0, 1);
9. addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(event:Event):void {
graphics.lineTo(Math.random( ) * 400, Math.random( ) * 400);
} } }
1.6. 响应鼠标和键盘事件
问题
我要处理鼠标或键盘事件
解决办法
监听和捕获处理鼠标和键盘事件
23
讨论
01
处理鼠标和键盘事件很类似于enterFrame 事件,这些在1.5节已经讨论过,只是略有不同。对于
ye
鼠标事件,主程序不会直接接收, 需要通过一个可视组件监听它 (关于可视组件会在第5章讨论 )。 in
ix
下面的例子创建一个sprite,添加到可视组件列,然后在它上面画了个矩形:
/l
et
package {
.n
dn
import flash.display.Sprite;
cs
/b !
g.
import flash.events.MouseEvent;
:/ 译
lo
tp 翻
public class ExampleApplication extends Sprite {
ht 青
常
private var _sprite:Sprite;
public function ExampleApplication( ) {
_sprite = new Sprite( );
addChild(_sprite);
_sprite.graphics.beginFill(0xffffff);
_sprite.graphics.drawRect(0, 0, 400, 400);
_sprite.graphics.endFill( );
注意:鼠标事件名称被定义在 MouseEvent 类中,事件处理函数需要传递进一个MouseEvent 类
实例,现在为sprite加入鼠标监听器:
_sprite.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
_sprite.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
10. 接着,定义两个处理函数onMouseDown 和 onMouseUp:
private function onMouseDown(event:MouseEvent):void {
_sprite.graphics.lineStyle(1, 0, 1);
_sprite.graphics.moveTo(mouseX, mouseY);
_sprite.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
}
private function onMouseUp(event:MouseEvent):void {
_sprite.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
}
onMouseDown 方法设置画线的类型,移动画刷到鼠标点击位置,然后添加了第三个鼠标监听
器监听 MouseMove 事件
onMouseUp 方法用removeEventListener 方法移除监听器,它和addEventListener 方法具有相同
语法结构,只是作用相反罢了
23
01
最后,定义onMouseMove 函数
ye
private function onMouseMove(event:MouseEvent):void {in
ix
/l
_sprite.graphics.lineTo(mouseX, mouseY);
et
.n
}
dn
}
cs
/b !
g.
}
:/ 译
lo
tp 翻
这样就建立了一个事件驱动的绘画程序。
ht 青
常
键盘事件的处理简单一些,只需要监听和响应键盘事件,接受这些事件的对象必须出于激活状
态。我们需要在主程序中加入这一行:
stage.focus = this;
下面的例子展示一个简单的类,它监听键盘的 keyDown 事件,输出按键的字符码,
package {
import flash.display.Sprite;
import flash.events.KeyboardEvent;
public class ExampleApplication extends Sprite {
public function ExampleApplication( ) {
stage.focus = this;
addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
}
12. quantity = quantity / factor;
quantity /= factor;
如果只是增加1或减少1,还可以象下面这样写
这个语句让quantity 增加1:
quantity++;
下面的两个语句效果相同
quantity += 1;
这个语句让 quantity 减去1:
quantity --;
下面的两个语句效果相同:
quantity = quantity 1;
quantity -= 1;
23
自增和自减运算符还有前缀写法:
01
ye
var quantity:Number = 5;
in
ix
trace(quantity++); // Displays: 5
/l
et
trace(quantity); // Displays: 6
.n
var quantity:Number = 5;
dn
cs
trace(++quantity); // Displays: 6
/b !
g.
:/ 译
lo
trace(quantity); // Displays: 6
tp 翻
ht 青
回到起初的问题,你可以用这些操作符修改属性值。下面的代码指定了sprite每帧角度加5:
常
private function onEnterFrame(event:Event) {
_sprite.rotation += 5;
}
13. 1.8. 逻辑运算
问题
我想检测两个值的大小
解决办法
使用==号来比较两个值,使用isNaN( )来检测是否是有效值.
讨论
==号表达式总是返回布尔值来表示两个值是否相等。当两个数类型不同时,比较时会自动转换
为相同的类型再进行比较,如字符型的6和数字型的6比较的话被认为相等。
trace(5 == 6); // : false
trace(6 == 6); // : true
trace(6 == "6"); // : true
23
trace(5 == "6"); // : false
01
ye
默认的工程项目,在运行上面的代码会出错。因为编译器被设置为强类型编译检测。关掉强类
型检测,会把数字型转换为字符型,然后再进行比较。一般不推荐关闭强类型检测,这样可能会
in
ix
/l
引发一些隐蔽的错误不利于程序稳定。
et
当两个数值不相等时,!=操作符将返回true,否则为false
.n
dn
trace(5 != 6); // : true
cs
/b !
g.
trace(6 != 6); // : false
:/ 译
lo
tp 翻
trace(6 != "6"); // : false
ht 青
常
trace(5 != "6"); // : true
同样,只有在关闭强类型检测后才能编译通过。
平时要注意不要把==写成=,否则会出现无法预料的错误。比如:
var quantity:int = 5;
// 下面的代码是错误的,正确应为 if (quantity == 6)
if (quantity = 6) {
trace("Rabbits are bunnies.");
}
trace("quantity is " + quantity); // 输出: quantity is 6
可以使用 is 操作符来检测数据类型
var quantity:int = 5;
if (quantity is int) {
14. trace("Yippee. It's an integer.");
}
然而有些数值是非法的。下面的代码中quantity 等于 NaN (一个表示无效数字的常数)
var quantity:Number = 15 - "rabbits";
NaN 虽然是无效的数值,但它的数据类型仍属于 Number ,
trace(typeof quantity); // 显示: "number"
所以,为了测试有个number变量类型不是数字,但又是合法的number,尝试下这么写:
var quantity:Number = 15 - "rabbits";
if (quantity is Number) {
//看起来好像正确,实际上是错误的,因为quantity != NaN 结果被认为都是false
if (quantity != NaN) {
trace("Yippee. It's a number.");
23
}
01
ye
}
in
ix
为了检测一个数字是不合法的,要使用指定的函数isNaN( ) 看下面的例子:
/l
et
var quantity:Number = 15 - "rabbits";
.n
if (isNaN(quantity)) {
dn
cs
trace("Sorry, that is not a valid number.");
/b !
g.
:/ 译
lo
}
tp 翻
ht 青
如果要检测相反条件,只要取反就可以,比如为了检测一个变量是个合法的 number,使
常
用!isNAN( ),如下:
var quantity:Number = 15 - "rabbits";
if (!isNaN(quantity)) {
trace ("That is a valid number.");
}
当然了你还可以使用<和>比较符号来比较两个值得大小。
trace(5 < 6); // 显示: true
trace(5 > 5); // 显示: false
还有<= 和>= 符号
trace(5 <= 6); // 显示: true
trace(5 >= 5); // 显示: true
15. ActionScript 数据类型的比较有两个情况。在ActionScript中,数据类型分为两类:基本类型
(string, number, and Boolean) 和复合类型(object, sprite, and array)。当比较基本类型时,是比较
他们的值,下面的例子中 quantity 和 total 被认为是相等的因为他们包含相同的值6
var quantity:Number = 6;
var total:Number = 6;
trace (quantity == total); // 显示: true
然而,当比较符合数据类型时是通过他们的“引用”来比较。当两个引用所指向的对象完全相
同才被认为是相等的,而不仅仅是对象的内容相同。例如,两个数组包含相同的内容,但是他
们却不相等:
// 用相同的内容创建两个数组
var arrayOne:Array = new Array("a", "b", "c");
var arrayTwo:Array = new Array("a", "b", "c");
trace(arrayOne == arrayTwo); // 显示: false
23
只要当引用指向同一个object, array, 或 sprite 才相等. 例子:
01
ye
// 创建一个简单的数组
in
ix
var arrayOne:Array = new Array("a", "b", "c");
/l
et
// 创建另一个变量指向同一个数组
.n
dn
var arrayTwo:Array = arrayOne;
cs
trace(arrayOne == arrayTwo); // 显示: true
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
16. 1.9. 执行条件语句
问题
我要当满足某些条件时才执行一些命令
解决办法
使用 if 或 switch 语句
讨论
我们经常需要让代码去有选择性的执行,这时可以使用 ActionScript 中的条件语句 if, switch,
或 三元条件运算符 (? :).
条件语句允许我们做出逻辑判断,某种情况下应该做什么。if语句是最简单的判断语句,当我们
遇到多个可能的情况要处理,这时用switch更好些。而三元条件运算符是把检测和赋值都放在一
行中搞定,简化操作。
首先我们来看一下 if 语句, if 语句以if关键字开头,接着跟一对括号,括号内为测试表达式,
后面的大括号放入测试表达式成立时要执行的代码。
23
01
下面的代码检测 animalName 是否等于"turtle."
ye
if (animalName == "turtle") { // 如果相等 trace( ) 语句将被执行 in
ix
/l
trace("Yay! 'Turtle' is the correct answer.");
et
.n
}
dn
另外还可以加上 else 子句来处理当测试表达式不满足时的情况,要注意的是要看到trace()输出
cs
/b !
g.
的信息则必须让程序在debug模式下运行。我们把输出信息放到showMessage( ) 方法里,这样这
:/ 译
lo
个函数就可以被重用了
tp 翻
ht 青
if (animalName == "turtle") { // 条件为真则执行
常
showMessage("Yay! 'Turtle' is the correct answer.");
}else { // 条件为假
showMessage("Sorry, you got the question wrong.");
}
还可以加入else if 子句,如果 if 条件为真则跳过 else if 子句,如果为假则继续判断 else if 字
句是否为真
if (animalName == "turtle") { // 条件为真则执行
showMessage ("Yay! 'Turtle' is the correct answer.");
}else if (animalName == "dove") { //animalName == "dove"成立则执行
showMessage ("Sorry, a dove is a bird, not a reptile.");
}
17. 还可以包含更多的 else if 子句,然而这种情况,最好的办法就是采用 switch 语句代替,因为
switch 与 if 语 句 结 构 更 加 清 晰 和 简 洁 。 statements are more legible and succinct than the
comparable if statement. 但在某些特殊场合,用 if 可以达到优化性能的目的。
switch 语句包含三部分:
switch 关键字
每个switch 语句都以switch 关键字开始
测试表达式
测试表达式被括号包围,它的结果将决定执行哪段代码。
switch 语句主体
主体中一般包含多个cases子句或一个default 子句
Case 表达式
case表达式将和 switch 表达式进行比较,如果相等就执行当前case的主代码。.
Case 主体y
23
01
当所有的case表达式都不等于switch表达式,将执行default主体。
ye
switch (testExpression) { in
ix
/l
case caseExpression:
et
.n
// case body
dn
case caseExpression:
cs
/b !
g.
// case body
:/ 译
lo
tp 翻
default:
ht 青
常
// case body
}
例子:
var animalName:String = "dove";
switch (animalName) {
case "turtle":
trace("Yay! 'Turtle' is the correct answer.");
case "dove":
trace("Sorry, a dove is a bird, not a reptile.");
default:
trace("Sorry, try again.");
}
18. 一般情况下,在每个case主体最后都会加上 break 语句,这样执行完就会直接退出switch语 句 。
var animalName:String = "dove";
// 现在第2个case主体将被执行
switch (animalName) {
case "turtle":
trace("Yay! 'Turtle' is the correct answer.");
break;
case "dove":
trace("Sorry, a dove is a bird, not a reptile.");
break;
default:
trace("Sorry, try again.");
23
}
01
ye
当有多个匹配但是执行代码是一样的,这时可以这么写:
in
ix
switch (animalName) {
/l
et
case "turtle":
.n
case "alligator":
dn
cs
case "iguana":
/b !
g.
:/ 译
lo
trace("Yay! You named a reptile.");
tp 翻
ht 青
break;
常
case "dove":
case "pigeon":
case "cardinal":
trace("Sorry, you specified a bird, not a reptile.");
break;
default:
trace("Sorry, try again.");
}
ActionScript 还支持三元条件运算符 (? :), 它把条件测试和赋值语句都放在一行完成。总共有3
个操作数,第一个是条件表达式,如果为真,则取第二个操作数为结果,否则去第三个操作数
为结果。
varName = (conditional expression) ? valueIfTrue : valueIfFalse;
19. 1.10. 执行复杂的条件语句
问题
我要在多个条件中做出决定
解决办法
可以使用逻辑运算符AND (&&), OR (||), 和 NOT (!) 来创建符合条件语句。
讨论
ActionScript中的很多语句都能包含条件表达式。包括 if, while, 和 for 语句,如果测试两个条件
都成立可以使用逻辑运算符 AND , &&, (更多细节请看第14章):
// 测试今天是否是3月17号
var current:Date = new Date( );
if (current.getDate( ) == 17 && current.getMonth( ) == 3) {
23
trace ("Happy Birthday, Bruce!");
01
ye
}
in
ix
加入些括号让结构更加清晰:
/l
et
// Check if today is April 17th.
.n
dn
if ((current.getDate( ) == 17) && (current.getMonth( ) == 3)) {
cs
trace ("Happy Birthday, Bruce!");
/b !
g.
:/ 译
lo
}
tp 翻
ht 青
这里使用了逻辑运算符OR , ||, 来测试是否其中有个条件成立:
常
// 测试是否是周末
if ((current.getDay( ) == 0) || (current.getDay( ) == 6) ) {
trace ("Why are you working on a weekend?");
}
还可以使用 NOT, !, 来测试条件不是真的:
// 检测名字不是Bruce.
if (!(userName == "Bruce")) {
trace ("This application knows only Bruce's birthday.");
}
上面的例子还可以这么写:
if (userName != "Bruce") {
20. trace ("This application knows only Bruce's birthday.");
}
任何布尔值或能得出布尔结果的表达式都能作为测试表达式:
// 检测如果sprite 是可见的,则输出信息
if (_sprite.visible) {
trace("The sprite is visible.");
}
NOT 运算符经常被用来检测是否是false:
// 检测如果 sprite 是不可见的,则输出信息:
if (!_sprite.visible) {
trace("The sprite is invisible. Set it to visible before trying this action.");
}
23
NOT 经常和OR 一起用:
01
ye
// 检测既不是Bruce 有不是 Joey.
in
ix
if (!((userName == "Bruce") || (userName == "Joey"))) {
/l
et
trace ("Sorry, but only Bruce and Joey have access to this application.");
.n
}
dn
cs
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
21. 1.11. 某段时间重复执行一种操作
问题
我要在单帧里多次执行某个任务
解决办法
在单帧里使用循环语句多次执行某个任务,例如,使用 for 语句:
for (var i:int = 0; i < 10; i++) {
// 显示i.的值
TRace(i);
}
讨论
使用循环语句可以让你的代码更加简洁。 容易阅读和维护。既可以用 while 也可以用 for 语句 ,
23
但是一般for语句比较好用。两个循环语句都能达到相同结果,只是 for 语句对于大多数程序员
01
来说更熟悉些。
ye
原形:
in
ix
/l
for (initialization; test; update) {
et
.n
statement body
dn
cs
}
/b !
g.
:/ 译
lo
下面的例子输出0到999的数字:
tp 翻
ht 青
for (var i:int = 0; i < 1000; i++) {
常
trace(i);
}
trace ("That's the end.");
多个初始值或步进值可以用逗号分开,初始化多个变量var 关键字只需要使用一次,下面的例
子展示了每次i增加1,j减小1,然后输出i和j:
for (var i:int = 0, j:int = 10; i < 10; i++, j--) {
trace("i is " + i);
trace("j is " + j);
}
for 语句还可以嵌套,看下面的例子:
for (var i:int = 1; i <= 3; i++) {
for (var j:int = 1; j <= 2; j++) {
22. trace(i + " X " + j + " = " + (i * j));
}
}
1X1=1
1X2=2
2X1=2
2X2=4
3X1=3
3X2=6
进行多重嵌套的for语句:
for (var i:int = 1; i <= 3; i++) {
for (var j:int = 1; j <= 3; j++) {
23
for (var k:int = 1; k <= 3; k++) {
01
ye
trace(i + " X " + j + " X " + k + " = " + (i * j * k));
in
ix
}
/l
et
}
.n
}
dn
cs
许多开发都错误的用 for 语句让sprites运动起来; 比如:
/b !
g.
:/ 译
lo
for (var i:int = 0; i < 20; i++) {
tp 翻
ht 青
_sprite.x += 10;
常
}
上面的代码让sprite 向右移动200 像素,所有的更新都在同一帧完成,会出现两个问题:第一,
场景每帧更新一次, 所以只有最后的更新显示在场景中 (导致我们看到好像是直接跳过200像素 ,
而不是20步内慢慢移动过去) 。第二,即使场景不停更新,但是for循环只需要几位秒,这样的
动画也太快了。因此正确的做法是把动画放到 enterFrame 事件上执行。
再者若循环的代码执行时间超过15秒,Flash播放器就会提示警告。
23. 1.12. 长时间执行一个任务
问题
我要长时间执行一个任务
解决办法
使用Timer类,或者监听sprite的enterFrame事件
讨论
Timer 类是ActionScript 3.0新增的, 来代替早期的 setInterval( ) 和 setTimeout( ) 函数。当创建
Timer类的实例时,它会在每个时间间隔激活 timer 事件,你可以在事件之间指定延时,然后就
有足够的时间去激活 Timer 构造器了:
var timer:Timer = new Timer(delay, repeatCount);
使用 addEventListener 来设置一个函数处理这个事件,然后使用timer的 start( ) 方法启动或
stop( ) 停止它。
23
01
Timer 类属于 flash.utils 包,还有 TimerEvent 类在 flash.events 包中,因此需要导入它们:
ye
package { in
ix
/l
import flash.display.Sprite;
et
.n
import flash.events.TimerEvent;
dn
import flash.utils.Timer;
cs
/b !
g.
public class ExampleApplication extends Sprite {
:/ 译
lo
tp 翻
private var _PreviousTime:Number = 0;
ht 青
常
public function ExampleApplication( ) {
var tTimer:Timer = new Timer(500, 10);
tTimer.addEventListener(TimerEvent.TIMER, onTimer);
tTimer.start( );
}
private function onTimer(event:TimerEvent):void {
trace(flash.utils.getTimer( ) - _PreviousTime);
_PreviousTime = flash.utils.getTimer( );
}
}
}
getTimer( ) 函数已经被移动到flash.utils 包中了。它返回程序开始有到现在的相对时间(微妙)
24. 上个例子中,事件每隔5毫秒激活一次。如果你想模拟 setInterval( ) 函数,把重复次数设为0。
stop( ) 方法类似于clearInterval( ) 函数,停止定时器.
如果想模拟setTimeout( )函数,设置重复数为1,定时器等到指定时间激活一次事件,然后停止。
Timer类最好的用处就是创建动画而不依赖于影片帧速。看下面的例子,两个定时器时间间隔分
别为50微妙和100微妙:
package {
import flash.display.Sprite;
import flash.events.TimerEvent;
import flash.utils.Timer;
public class ExampleApplication extends Sprite {
private var _square:Sprite;
private var _circle:Sprite;
public function ExampleApplication( ) {
23
01
// 创建两个图形
ye
_square = new Sprite( ); in
ix
/l
_square.graphics.beginFill(0xff0000);
et
.n
_square.graphics.drawRect(0, 0, 100, 100);
dn
_square.graphics.endFill( );
cs
/b !
g.
addChild(_square);
:/ 译
lo
tp 翻
_square.x = 100;
ht 青
常
_square.y = 50;
_circle = new Sprite( );
_circle.graphics.beginFill(0x0000ff);
_circle.graphics.drawCircle(50, 50, 50);
_circle.graphics.endFill( );
addChild(_circle);
_circle.x = 100;
_circle.y = 200;
// 创建两个定时器,启动
var squareTimer:Timer = new Timer(50, 0);
squareTimer.addEventListener(TimerEvent.TIMER, onSquareTimer);
squareTimer.start( );
25. var circleTimer:Timer = new Timer(100, 0);
circleTimer.addEventListener(TimerEvent.TIMER, onCircleTimer);
circleTimer.start( );
}
// 定义两个事件句柄
private function onSquareTimer(event:TimerEvent):void {
_square.x++;
}
private function onCircleTimer(event:TimerEvent):void {
_circle.x++;
}
}
23
}
01
ye
当然用 enterFrame 事件也可以实现的,但 Timer 技术更加灵活。
in
ix
/l
et
.n
dn
cs
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
26. 1.13. 创建可重用代码
问题
我要实现代码重用,而不是每次都去复制同样的代码。
解决办法
创建一个方法,然后再需要的地方调用它,类中的函数 我们通常称为方法。
怎样创建类方法:
控制修饰符 function 方法名 ( ):返回数据类型 {
// 代码块
}
调用该方法只要饮用方法名就可以了,比如:
方法名( );
23
讨论
01
ye
方法中的代码可以被多次执行。当你需要在不同的时间不同的地方执行同一个任务时就会很有
用。把代码放在方法既便于理解又便于维护,而不用再多个地方修改。
in
ix
/l
像类变量一样,方法也有访问控制符。修饰符有:
et
.n
private
dn
cs
只能被自身类访问。
/b !
g.
:/ 译
lo
protected
tp 翻
能被自身类实例或子类实例访问,其他类实例不能访问。
ht 青
常
internal
可以被所在包中的所有类实例访问。
public
可被任何类访问。
如果都没指定修饰符,默认为internal。下面的代码定义了一个画线方法,然后被调用10次。
package {
import flash.display.Sprite;
public class ExampleApplication extends Sprite
{
public function ExampleApplication( ) {
for(var i:int=0;i<10;i++) {
drawLine( );
27. }
}
private function drawLine( ):void {
graphics.lineStyle(1, Math.random( ) * 0xffffff, 1);
graphics.moveTo(Math.random( ) * 400, Math.random( ) * 400);
graphics.lineTo(Math.random( ) * 400, Math.random( ) * 400);
}
}
}
还有种种要的方法类型是静态方法,静态方法不属于类实例,可以通过类直接调用静态方法。
比如,有个类叫ExampleApplication, 定义了静态方法:
public static function showMessage( ):void {
23
trace("Hello world");
01
ye
}
可以这样调用:
in
ix
/l
ExampleApplication.showMessage( );
et
.n
有些类只有静态方法,Math 类就是个例子,注意我们使用 Math 方法时并没有创建类实例,我
dn
们只是调用了类属性那样调用类方法,比如 Math.random( ), Math.round( ), 等等
cs
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
28. 1.14. 增强代码可重用能力
问题
每次执行的任务都有微小的变化,但我又不想每次都复制那些代码修改一次。
解决办法
给方法传递参数让它适应不同的情况。
private function average (a:Number, b:Number, c:Number):void {
trace("The average is " + (c + b + c)/3);
}
讨论
比如你有个求一系列数的平均数函数average( ) ,你就可以把这些数字作为参数传递给函数去计
算,而不必每次都去重写average( )函数。
23
通常把参数都列在申明函数的括号内,多个参数用逗号分开。
01
下面有个简单的带有参数的函数申明:
ye
//定义函数,带有两个参数: a 和 b.
in
ix
/l
private function average(a:Number, b:Number):Number {
et
.n
return (a + b)/2;
dn
cs
}//当函数被调用时,参数被传递进来,比如 5 和 11, 被传递给了 a 和 b
/b !
g.
:/ 译
lo
var averageValue:Number = average(5, 11);
tp 翻
大多数情况下,方法参数的个数可以预料的,但是有些情况下参数的个数是事先不确定的。比
ht 青
常
如:如果你想要让average( ) 方法接受任何数量的值,这时可以使用内建的数组,所有的参数都
被放入函数数组中。
// arguments 数组
private function average( ):Number {
var sum:Number = 0;
for (var i:int = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum/arguments.length;
} // 像下面这样传递任意数量的参数:
var average:Number = average (1, 2, 5, 10, 8, 20);
arguments 是一个 array 对象。
29. 1.15. 从方法中退出
问题
我要从方法中退出
解决办法
方法中的代码被执行完就会自动退出,也可使用return语句直接退出。
讨论
return 语句将导致方法立即退出,ActionScript 解释器继续执行调用方法的所在位置的下面代
码。方法中return下面的语句将被忽略。
private function sampleFunction ( ):void {
return;
trace("Never called");
23
}
01
ye
下面的代码展示如果密码是错误的,则从方法中退出:
in
ix
private function checkPassword (password:String):void {
/l
et
// 如果密码不是"SimonSays", 退出方法
.n
dn
if (password != "SimonSays") {
cs
return;
/b !
g.
:/ 译
lo
}
tp 翻
ht 青
//否则执行剩余的代码
常
showForm ("TreasureMap");
}
// 使用错误的密码调用函数,所以函数退出
checkPassword("MotherMayI");
//使用正确的密码,所以显示“TreasureMap”信息.
checkPassword("SimonSays");
你可能注意到上面的例子方法被申明为void, 如果用return语句只是简单的退出这时可以的,但
如果想返回值得话编译器就会报错了,如:
private function sampleMethod ( ):void {
return "some value"; // This causes the compiler to generate an error.
}
30. 1.16. 获得方法的执行结果
问题
我想执行一些方法,然后返回结果给调用它的函数
解决办法
使用 return 语句返回结果
讨论
return 返回的数据类型必须与函数申明的返回类型相一致。
private function average (a:Number, b:Number):Number {
return (a + b)/2;
}
现在我们调用 average( ) 方法 然后把返回结果存到变量中,然后使用这个变量:
23
01
var playerScore:Number = average(6, 10);
ye
trace("The player's average score is " + playerScore); in
ix
也可以不通过变量:
/l
et
trace("The player's average score is " + average(6, 10));
.n
dn
注意,如果你不处理返回的值,那么返回结果就是丢失掉:
cs
average(6, 10);
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
31. 1.17. 处理异常
问题
我想让程序自己检测和处理遇到的异常。
解决办法
当检测到错误时使用 throw 语句抛出异常。把可能出现错误的代码都放到 try 块中,然后在
catch 块中进行错误处理。
讨论
Flash 播放器 8.5 开始支持 try/catch 方法来处理错误。这意味着可以灵活的处理遇到的错误
了。除了语法错误(这时编译器就通不过) ,其他类型的错误如非法数据等都可以自己处理。
处理异常包括两个部分,抛出异常和捕获异常。有些异常系统会自动抛出,比如
IllegalOperationError, MemoryError, 和 ScriptTimeoutError. 它们都在 flash.errors 包中。除了系
统定义的错误外也可以抛出自定义错误,然后捕获它进行处理。使用 throw 语句抛出一个Error
23
对象或Error 子类实例,比如:
01
ye
throw new Error("A general error occurred.");
in
ix
正如我们看到的, Error 构造器接受一个参数,这个信息和这个错误相关联。这个参数是可选
/l
的,依赖于你怎样处理这个错误,你可以不使用,但是大多数情况下都指定一个错误信息作为
et
.n
调试目的。
dn
一旦异常被抛出,Flash就会暂停当前进程去寻找 catch 块去处理异常。任何有潜在错误的代码
cs
/b !
g.
都要放在 try 块中,如果异常抛出,只有它所在的 try 块被暂停,然后相关联的 catch 块被调
:/ 译
lo
用,看下面的例子:
tp 翻
ht 青
try {
常
trace("This code is about to throw an error.");
throw new Error("A general error occurred.");
trace("This line won't run");
}
catch (errObject:Error) {
trace("The catch block has been called.");
trace("The message is: " + errObject.message);
}
上面的代码数出以下信息:
This code is about to throw an error.
The catch block has been called.
32. The message is: A general error occurred.
当然,上面的代码还是过于简单,但是这是个基本框架,可以看到只要抛出异常,try 块就会退
出,catch 块被执行,传递了一个 Error 对象给 catch.
更多情况下,异常是从函数或方法中抛出的,Flash 会检测该函数是否在 try 块内被调用,如
果是,则调用相应的 catch 块。
private function displayMessage(message:String):void {
if(message == undefined) {
throw new Error("No message was defined.");
}
trace(message);
}
try {
trace("This code is about to throw an error.");
23
01
displayMessage( );
ye
trace("This line won't run"); in
ix
/l
}
et
.n
catch (errObject:Error) {
dn
trace("The catch block has been called.");
cs
/b !
g.
trace("The message is: " + errObject.message);
:/ 译
lo
tp 翻
}
ht 青
常
上面的代码输出以下内容:
This code is about to throw an error.
The catch block has been called.
The message is: No message was defined.
如果你不肯定你的函数或方法会在何时或如何抛出异常,这时就应该在try块进行调用。
// 定一个在指定的sprite里的画矩形函数。
private function drawRectangle(sprite:Sprite, newWidth:Number, newHeight:Number):void {
// 检测长和宽的数值是否合法,否则抛出异常。
if(isNaN(newWidth) || isNaN(newHeight)) {
throw new Error("Invalid dimensions specified.");
}
// 如无异常,则画出矩形
33. sprite.graphics.lineStyle(1, 0, 1);
sprite.graphics.lineTo(nWidth, 0);
sprite.graphics.lineTo(nWidth, nHeight);
sprite.graphics.lineTo(0, nHeight);
sprite.graphics.lineTo(0, 0);
}
现在我们在 try/catch 语句内调用该函数。
try {
drawRectangle(this, widthB, heightB);
}
catch(errObject:Error) {
this.graphics.clear( );
23
tOutput.text = "An error occurred: " + errObject.message;
01
ye
}
in
ix
另外对于try/catch 语句,还可以加入 finally 块,finally 块包含的代码无论是否遇到异常都会
/l
被执行。例如下面的两个例子效果相同:
et
.n
//没有使用finally:
dn
cs
private function displayMessage(message:String):void {
/b !
g.
:/ 译
lo
try {
tp 翻
ht 青
if(message == undefined) {
常
throw new Error("The message is undefined.");
}
trace(message);
}
catch (errObject:Error) {
trace(errObject.message);
}
trace("This is the last line displayed.");
}
//使用 finally:
private function displayMessage(message:String):void {
try {
34. if(message == undefined) {
throw new Error("The message is undefined.");
}
trace(message);
}
catch (errObject:Error) {
trace(errObject.message);
}
finally {
trace("This is the last line displayed.");
}
}
23
如果在catch中使用了return语句,那结果就不一样了:
01
ye
//没有使用finally:
in
ix
private function displayMessage(message:String):void {
/l
et
try {
.n
if(message == undefined) {
dn
cs
throw new Error("The message is undefined.");
/b !
g.
:/ 译
lo
}
tp 翻
ht 青
trace(message);
常
}
catch (errObject:Error) {
trace(errObject.message);
return;
}
// 这一句没有执行.
trace("This is the last line displayed.");
}
//使用 finally:
private function displayMessage(message:String):void {
try {
35. if(message == undefined) {
throw new Error("The message is undefined.");
}
trace(message);
}
catch (errObject:Error) {
trace(errObject.message);
return;
}
finally {
// 执行,不管是否有异常发生。
trace("This is the last line displayed.");
23
}
01
ye
}
in
ix
通过这一节的学习,现在你可以建立复杂的异常处理系统。
/l
et
.n
dn
cs
/b !
g.
2.0. 简介
:/ 译
lo
tp 翻
ht 青
ActionScript 3.0 最本质的东西就是类, 也就说它是面向对象的。 ActionScript 3.0 在面向对象基
常
础上 重 新 构 建 了 ActionScript 核心 。 如 果 在 Flex 上编 写 ActionScript 3.0 ,代 码 都 被 放 在
<mx:Script> 标签内,所有 ActionScript 都必须以类的形式出现。这一章讨论在 ActionScript 3.0
上编写自定义类。
36. 2.1. 创建自定义类
问题
我要编写自己的类
解决办法
保存一个以.as扩展名的新文件,类名和文件名相同,编写如下结构:
package package {
public class Class {
}
}
讨论
在ActionScript 3 中,类是最基本的编程结构,所以必须先掌握编写类的基础知识。对于初学者 ,
23
所以得类都必须放在.as文件中,每个as文件里只能定义一个public 类,而且类名字要与文件名
01
相同。比如:你的类名为 Example ,那么文件名必须为 Example.as.
ye
在 ActionScript 3.0 中所有的类都必须放在包中。包是对类进行分类的单位,其意义相当于文件
in
ix
/l
系统的目录。 包路径相对于classpath
(类路径),默认的类路径就是项目的根目录(就是包含mxml
et
文件的所在目录) ,因此顶级的包目录就是项目根目录。包申明如下:
.n
dn
package name {
cs
/b !
g.
}
:/ 译
lo
tp 翻
如果类定义在顶级包中,那么包名可以不指定,如:
ht 青
常
package {
}
当类文件保存在子目录,那么包名就是它的保存目录,例如,文件保存在example目录,那么包
这样申明:
package example {
}
如果类文件保存在 example 目录的子目录 subpackage, 应这样申明:
package example.subpackage {
}
包是很重要的,它可以避免类名称空间冲突。例如,有两个开发者写了两个类文件都叫
MessageManager. 这两个类虽有相同名字,但是完成不同的任务,因此你不能把这两个类放在
一起,如果这样做,编译器将不知道调用哪个,一个办法是取个唯一的类名字。
你可以取名字叫 EmailManager 和 BinarySocket- MessageManager, 这是可以的,但是如果你管
37. 理成千上万的类这时就很困难了。因此用包可以很好的解决这个问题,即使你有很多相同的类
名,只要它们不在同一个包就不会冲突,如把 MessageManager 放在net.messaging.email 包另
一个放在net.messaging.binarysocket 包中。
一般取包名都以自己的网站域名,这样可以最大限度避免和别人的包名相冲突。
当有多个项目公用一些类,那么这些类直接被放在主包 中的子目录中。例如,上面的
MessageManager 类 放 在 com.examplecorp.net.messaging.email 和
com.examplecorp.net.messaging.binary- socket 包中。
下一步就是申明类自身:
public class Name {
}
类申明必须在包内。下面的代码在顶级包中定义了叫 Example 的类:
package {
public class Example {
23
}
01
ye
}
类主体在括号内定义,包括属性,方法。属性就是和类关联的变量,使用var关键字申明他们,
in
ix
/l
属性也有修饰符指定其范围。修饰符有:
et
private
.n
dn
该属性只有类实例自身可访问.
cs
/b !
g.
public
:/ 译
lo
该属性可以被任何类实例访问(若直接被类访问可设置成static)
tp 翻
ht 青
protected
常
该属性只被自身类实例或派生类实例访问。
internal
该属性可被包内的类实例访问。
默认情况下是属性被指定为 internal , 除非自己指定修饰符。 大多数情况,属性被指定为 private
或protected。按照习惯约定,private 和 protected 申明的属性名称都在前面加上下划线。看下
面的例子:
package {
public class Example {
private var _id:String;
}
}
与类关联的还有方法,你可以使用function关键字像申明函数那样申明方法。和属性一样,方法
38. 也有修饰符(public, private, protected, internal)。如果方法被类实例访问可设置为public(直接被
类访问则加上static).如果方法只在类内方法则被设置为 private 或 protected。下面的代码申明
一个方法叫getId( ):
package {
public class Example {
private var _id:String;
public function getId( ):String {
return _id;
}
}
}
按照约定,方法名称的起始字符必须为小写。每个类都有个和自己类名相同的方法,该方法称
为构造函数,用它为创建新的实例时进行初始化工作。在 ActionScript 3.0 中,所有的构造函数
23
都是public ,不像标准的方法,构造函数不能有返回值,也不能申明有返回类型。下面的代码申
01
明了构造函数:
ye
package {
in
ix
/l
public class Example {
et
.n
private var _id:String;
dn
cs
public function Example( ) {
/b !
g.
:/ 译
lo
_id = "Example Class";
tp 翻
ht 青
}
常
public function getId( ):String {
return _id;
}
}
}
下面展示如何构造一个新的 Example 类实例:
var example:Example = new Example( );
trace(example.getId( )); // 显示: Example Class
40. 2.3. 创建成员属性
问题
我要创建public 成员属性
解决办法
使用隐含 getters 和 setters.
讨论
正如2.1节中所说的那样属性应该被申明为 private 或 protected。 public 属性并不是什么好主
意,因为他不能体现封装性。要尽量做好封装,这意味着类不能暴露他的内部细节,public 属
性使开发者能轻易破坏类或类实例。下面的简单例子是用了Public 属性:
package {
public class Counter {
23
public var count:uint;
01
ye
public function Counter( ) {
count = 0;
in
ix
/l
}
et
.n
}
dn
cs
}
/b !
g.
:/ 译
lo
构造一个Counter实例, 然后改变count 属性值,如:
tp 翻
ht 青
var counter:Counter = new Counter( );
常
counter.count++;
但是,如果count属性被规定不能超过100,那么外部修改很可能无法保证,这样破坏了这个规
定,一个办法就是设置 getters 和 setters,如下:
package {
public class Counter {
private var _count:uint;
public function Counter( ) {
_count = 0;
}
public function getCount( ):uint {
return _count;
}
41. public function setCount(value:uint):void {
if(value < 100) {
_count = value;
}
else {
throw Error( );
}
}
}
}
另一个办法就是用隐式getters 和 setters. 隐式 getters 和 setters 就像申明方法那样,但是看起
来又像属性getter 语法如下:
23
public function get name( ):Datatype {
01
ye
}
setter 语法:
in
ix
/l
public function set name(value:Datatype):void {
et
.n
}
dn
cs
下面定义隐式 getter 和 setter 方法:
/b !
g.
:/ 译
lo
package {
tp 翻
ht 青
public class Counter {
常
private var _count:uint;
public function Counter( ) {
_count = 0;
}
public function get count( ):uint {
return _count;
}
public function set count(value:uint):void {
if(value < 100) {
_count = value;
}
else {
42. throw Error( );
}
}
}
}
counter.count = 5;
trace(counter.count);
2.4. 创建静态方法或属性
问题
23
01
我要创建的方法和属性不需要类实例就能直接访问。
ye
解决办法 in
ix
使用static修饰符申明属性或方法
/l
et
讨论
.n
dn
默认下属性和方法是属于实例的,例如 Example 类定义了 _id 属性和 getId( ) 方法,那么每
cs
个Example 实例都有自己的_id 属性和getId( ) 方法。但是有种情况你希望属性或方法是和类相
/b !
g.
:/ 译
lo
关联而不是类实例,也就说不管有多少个类实例,都只有一个公共属性或方法,这样的属性和
tp 翻
方法称为静态属性和方法。
ht 青
常
Flash 播放器的类中就有些这样的例子,比如 Math 类中定义了 round( ) 方法,round( ) 方法
就是个静态方法,因此可以通过类直接访问:
trace(Math.round(1.2345));
Math 类包含全部是静态方法,但是类也可以同时含有静态方法和实例方法及属性。比如 String
类有多数实例属性和方法,然而fromCharCode( ) 方法是静态的,该方法返回字符码。
下面的代码申明了一个静态的私有的属性_example:
static private var _example:String;
修饰符的顺序没有关系,比如static private 和 private static 是一样的.
static 最重要的用处就是在单态模式下,即类只能创建一个实例,单态类有一个 private static 属
性用来存储类实例,然后在一个 public static 方法中访问这个实例。
43. 2.5. 创建子类
问题
我要创建派生类
解决办法
使用extends关键字继承已有的类
讨论
如果新建的类和已有的类拥有公共特性但是又比已有类有更多细节,这时去重写所有的代码还
不如从已有类中继承公共特性再添加专有代码。这时这个新类就是已有类的一个子类。
使用extends关键继承超类:
public class Subclass extends Superclass
子类可以引用任何超类中的 public 或 protected 的属性和方法,private 属性和方法是不能够访
23
问的。
01
继承性是非常强大的功能,但是另一方面如何正确的使用继承也很重要。在写子类之前你要确
ye
定新类和超类是否已经有子类关系,这里有两种基本关系类型:继承和组合。你经常要确定类 in
ix
之间是"is a" 关系还是"has a" 关系:
/l
et
"Is a" 关系是继承关系。如一个应用程序管理着一个图书馆的藏书。
.n
"Has a" 关系是组合关系。大多数类使用组合,而且比继承更有伸缩性(然而需要更多代码)
。
dn
cs
例如一本书不是一个作者,但是它有一个作者。
/b !
g.
:/ 译
lo
图书馆有不同类型的藏品包括书和DVDs。很明显书和DVDs有不同的类型数据。书包括页数和
tp 翻
作者,而DVDs则有播放时间数,演员,导演等等,但是也有一些公共数据,比如所有图书馆都
ht 青
有用一些唯一的数字编号或命名规则来管理这些藏品. 而且每个种类都有标题或名称,这时你
常
可以定义个类来归纳这些公共信息:
package org.examplelibrary.collection {
public class LibraryItem {
protected var _ddc:String;
protected var _id:String;
protected var _name:String;
public function LibraryItem( ) {}
public function setDdc(value:String):void {
_ddc = value; }
public function getDdc( ):String {
return _ddc; }
44. public function setId(value:String):void {
_id = value; }
public function getId( ):String {
return _id; }
public function setName(value:String):void {
_name = value; }
public function getName( ):String {
return _name; }
}
}
书和DVDs 都是 LibraryItem的一个种类. 接下来很自然就是定义 Book 类和 DVD 类,继承自
LibraryItem,Book 类类似于:
23
package org.examplelibrary.collection {
01
ye
import org.examplelibrary.collection.LibraryItem;
public class Book extends LibraryItem {
in
ix
/l
private var _authors:Array;
et
.n
private var _pageCount:uint;
dn
cs
public function Book( ) {}
/b !
g.
:/ 译
lo
public function setAuthors(value:Array):void {
tp 翻
ht 青
_authors = value; }
常
public function getAuthors( ):Array {
return _authors; }
public function setPageCount(value:uint):void {
_pageCount = value; }
public function getPageCount( ):uint {
return _pageCount;
}
}
}
默认下可以继承任何类,但是如果你不要这个类再被继承,可以添加final修饰符申明该类:
final public class Example
45. 2.6. 覆盖超类方法
问题
我要对从父类继承过来的方法进行重新实现。
解决办法
父类的方法必须申明为public 或 protected。当申明子类的实现时使用 override 修饰符
讨论
通常子类继承父类的所有方法而不做任何修改,但有些情况,继承过来的方法需要重新申明,
实现与父类的方法不同。这时就要覆盖方法,该方法必须加上override 修饰符。如下面的例子,
首先定义一个父类Superclass:
package {
public class Superclass {
23
public function Superclass( ) {}
01
ye
public function toString( ):String {
return "Superclass.toString( )";
in
ix
/l
}
et
.n
}
dn
cs
}
/b !
g.
:/ 译
lo
下一步,定义Subclass 继承自Superclass:
tp 翻
ht 青
package {
常
public class Subclass extends Superclass {
public function Subclass( ) {}
}
}
默认情况下,Subclass 继承 Superclass 实现的toString( ) 方法:
var example:Subclass = new Subclass( );
trace(example.toString( )); // 显示: Superclass.toString( )
如果你要 toString( ) 方法返回不同的值,就要覆盖它,如下:
package {
public class Subclass extends Superclass {
public function Subclass( ) {}
override public function toString( ):String {
46. return "Subclass.toString( )";
}
}
}
覆盖方法,则该方法必须与父类的方法完全相同,包括参数数量和类型及返回类型,如果不一
致,编译器就会报错。
有时你要覆盖方法是为了实现完全不同的功能,但有时你只是要增加一些东西,这时你可以调
用父类方法:
super.methodName( );
23
01
2.7. 创建常量
ye
in
ix
问题
/l
et
我要怎么申明常量
.n
dn
解决办法
cs
/b !
g.
和申明属性差不多,只是在前面多了const 关键字
:/ 译
lo
tp 翻
讨论
ht 青
常量的值一旦定义就不可改变,这在有时候是很有用的。比如你有个复合的值需要经常用到,
常
这时就可以把它当作简单的标示直接引用。Math.PI 就是个常量,MouseEvent.MOUSE_UP也是
常量,包含mouseUp值,设定这些常量可以减少出错,比如下面的代码错误你难以觉察出来:
// 下面的代码没错,但是mouseUp 被写成了 mousUp,因此该代码无效。
addEventListener("mousUp", onMouseUp);
但是使用常量,编译器就会给出错误提示:
// 导致编译器错误
addEventListener(MouseEvent.MOUS_UP, onMouseUp);
常量也可以有static和public修饰符,这时常量一申明就要赋值:
static public const EXAMPLE:String = "example";
按照约定,常量都要大写,这样可以和其他变量或属性区别开来。
47. 2.8. 发送事件
问题
我要发送事件
解决办法
继承 flash.events.EventDispatcher 然后调用 dispatchEvent( ) 方法
讨论
事件在对象之间的通讯起到至关重要的作用, 有了它才能开发出功能强大的系统。 Flash Player 9,
的flash.events.EventDispatcher类有一套事件发送机制。所有的事件都继承自EventDispatcher (比
如 NetStream and Sprite). 如果你要定义个类要发送事件也要继承EventDispatcher,如:
package {
import flash.events.EventDispatcher;
23
public class Example extends EventDispatcher {
01
ye
}
}
in
ix
/l
EventDispatcher 类有个公共方法 addEventListener( ) 和 removeEventListener( ), 通过它的子类
et
来注册事件监听。 EventDispatcher 还有个 protected 方法 dispatchEvent( ) 来发送事件。
.n
dn
dispatchEvent( ) 方法至少需要 flash.events.Event 对象或 Event 的子类作为参数。
cs
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
3.0. 简介
Flash Player 9 关于控制运行时环境提供了更多的信。 flash.system.Capabilities 类有许多静态
The
方法返回关于播放器和计算机的信息,比如操作系统,语言,音频和视频。还有其他的类如
flash.display.Stage 和 flash.system.Security 控制其他一些元素如播放器右键菜单和设置对话框。
flash.display.Stage 类也控制影片剪辑缩放和对齐。
Flash Player 9 可能是自 Flash Player 7 之后最具重要意义的一次升级。它比以往提供了更多能
力控制上下文菜单。在 Flash Player 7 里,用 ContextMenu 类,可以控制右键菜单,可以添加或
删除菜单项。
49. 3.2. 检测操作系统
问题
我要知道客户端的操作系统。
解决办法
使用flash.system.Capabilities.os 属性
讨论
ActionScript 3.0中,flash.system.Capabilities.os 属性返回操作系统名称和版本字符串。值可能包
括Windows XP, Windows 2000, Windows NT, Windows 98/Me, Windows 95, 和 Windows CE. 在苹
果机上,字符串包括版本号,比如 Mac OS 9.2.1 或 Mac OS X 10.4.4.
你可能基于操作系统做一些特殊处理,比如,根据当前系统载入特定的图标,或只是记录下用
户的操作系统来统计。
下面的代码展示检测操作系统:
23
01
var os:String = System.capabilities.os.substr(0, 3);
ye
if (os == "Win") { in
ix
/l
// Windows-specific code goes here
et
.n
} else if (os == "Mac") {
dn
// Mac-specific code goes here
cs
/b !
g.
} else {
:/ 译
lo
tp 翻
// Must be Unix or Linux
ht 青
常
}
50. 3.3. 检测播放器类型
问题
我想知道播放器类型.
解决办法
使用 flash.system.Capabilities.playerType 属性.
讨论
播放器的类型有:
浏览器插件形式存在于 Mozilla 或 Firefox
ActiveX 控件形式存在于Internet Explorer
独立播放器
外部播放器,它与Flash IDE进行交互。
23
01
这些都是.swf 运行的环境,如果你要使用脚本进行交互,这就需要知道应用程序到底在Internet
ye
Explorer 或其他的浏览器运行。如果在独立播放器里运行,那么JavaScript等脚本就不管用了。 in
ix
检测 播 放 器 类 型 , 察 看 flash.system.Capabilities.playerType 的值 。 它 可 能 是 PlugIn, ActiveX,
/l
et
StandAlone, 和 External:
.n
dn
if(flash.system.Capabilities.playerType == "Plugin") {
cs
// do actions for Mozilla, etc. browsers
/b !
g.
:/ 译
lo
}
tp 翻
ht 青
else if(flash.system.Capabilities.playerType == "ActiveX") {
常
// do actions for IE
}
else {
// do actions for no browser
}
51. 3.4. 检测系统语言
问题
我想知道客户端系统使用什么语言和输入法
解决办法
使用 flash.system.Capabilities.language 属性和 flash.system.IME 类
讨论
flash.system.Capabilities.language 属性给出客户端系统的语言,返回两个 ISO-639-1 字符(如
"fr" 代表 French). 有些国家代码两个字符是不合适的,比如( "zh-CN" 代表 Simplified Chinese
和 "zh-TW" 代表 Traditional Chinese).
下面的代码展示如何使用语言属性:
// Example output: en-US
23
trace(flash.system.Capabilities.language);
01
ye
var greetings:Array = new Array( );
greetings["en"] = "Hello";
in
ix
/l
greetings["es"] = "Hola";
et
.n
greetings["fr"] = "Bonjour";
dn
cs
var lang:String = flash.system.Capabilities.language.substr(0, 2);
/b !
g.
:/ 译
lo
if (greetings[lang] == undefined) {
tp 翻
ht 青
lang = "en";
常
}
trace(greetings[lang]);
如果要创建国际化的Flash,可以把文本保存在数组里,根据语言动态显示,或者直接做成多个
Flash版本(每个语言一个),如 myMovie_en.swf, myMovie_es.swf, myMovie_fr.swf, 等.
//从 capabilities 对象上得到语言值
var lang:String = System.capabilities.language.substr(0, 2);
// 创建支持语言数组
var supportedLanguages:Array = ["en", "es", "fr"];
// 设置默认语言.
var useLang:String = "en";
//循环匹配,如果找到,设置 useLang
for (var i:int = 0; i < supportedLanguages.length; i++) {
52. if (supportedLanguages[i] == lang) {
useLang = lang;
break;
}
}
// 载入对应Flash
var movieURL:String = "myMovie_" + useLang + ".swf");
还有一点也很重要,比如用户使用的输入语言,比如中文,日文,韩文,输入这些字符需要输
入法,这时特定操作系统的一部分。
为 了 检 测 用 户 使 用 什 么 输 入 法 , flash.system.Capabilities.hasIME 返 回 true 或 false ,
flash.system.IME 类返回关于输入法的信息。 flash.system.IME.enabled 属性设置用户是否可以使
用输入法。在有些操作系统和版本上你可以发送字符串给 IME 来转换成正确的字符,接受 IME
的返回,但这不是所有操作系统都支持的,最好检测下先。
23
01
ye
in
ix
/l
et
.n
dn
cs
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
53. 3.5. 检测显示设置
问题
我要知道客户机的显示设置情况
解决办法
使用 system.capabilities 对象的 screenResolutionX 和 screenResolutionY 属性
讨论
screenResolutionX 和 screenResolutionY 属性返回桌面的显示分辨率:
trace(flash.system.Capabilities.screenResolutionX);
trace(flash.system.Capabilities.screenResolutionY);
// 1024
// 768
23
01
有了这些值,你可以决定怎样显示flash影片。这一点对于Flash播放器也是很重要的,例如,手
ye
机屏幕和电脑屏幕尺寸是不同的,因此你要根据情况载入不同的尺寸的内容。
in
ix
var resX:int = flash.system.Capabilities.screenResolutionX;
/l
et
var resY:int = flash.system.Capabilities.screenResolutionY;
.n
dn
if ( (resX <= 240) && (resY <= 320) ) {
cs
var url:String = "main_pocketPC.swf";
/b !
g.
:/ 译
lo
}
tp 翻
ht 青
else {
常
var url:String = "main_desktop.swf";
}
loader.load(new URLRequest(url));
利用分辨率还可以居中你的弹出窗口:
var resX:int = flash.system.Capabilities.screenResolutionX;
var resY:int = flash.system.Capabilities.screenResolutionY;
//设置窗口的宽和高
var winW:int = 200;
var winH:int = 200;
// 设置窗口起始坐标
var winX:int = (resX / 2) - (winW / 2);
55. 影片显示不下的会被剪切掉,使用下面的语句设置:
stage.scaleMode = StageScaleMode.NO_BORDER;
exactFit 模式缩放影片适应播放器,它改变了电影原始比例,如果必要,它会匹配播放器,这
样电影总是填充整个播放器,但是这样电影中的元素可能会扭曲,代码如下:
stage.scaleMode = StageScaleMode.EXACT_FIT;
noScale 模式即不进行缩放,保持原始比例。使用该模式不要忘了设置对齐方式(看 3.7 节 ):
stage.scaleMode = StageScaleMode.NO_SCALE;
scaleMode 属性值并不影响右键菜单里功能,不过你可以禁用菜单里的缩放功能,看3.8 节。
3.7. 改变对齐方式
23
01
ye
问题
in
ix
我要改变影片的对齐方式
/l
et
解决办法
.n
使用 stage.align 属性
dn
cs
讨论
/b !
g.
:/ 译
lo
默认下 Flash 电影会居中显示。 可以利用任何可视化对象的 stage.align 属性来重新设置电影的对
tp 翻
ht 青
齐方式。flash.display.StageAlign 类的属性:
常
Value Vertical alignment Horizontal
StageAlign.TOP Top Center
StageAlign.BOTTOM Bottom Center
StageAlign.LEFT Center Left
StageAlign.RIGHT Center Right
StageAlign.TOP_LEFT Top Left
StageAlign.TOP_RIGHT Top Right
StageAlign.BOTTOM_LEFT Bottom Left
StageAlign.BOTTOM_RIGHT Bottom Right
这里没有水平和垂直都居中的模式,其实,默认模式就是它了,但如果你改变了对其方式又想
回到默认模式这时后只能传递空字符串""。
下面的类展示了缩放和对齐效果:
package {
56. import flash.display.Sprite;
import flash.display.StageScaleMode;
import flash.display.StageAlign;
public class ExampleApplication extends Sprite {
public function ExampleApplication( ) {
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_RIGHT;
graphics.beginFill(0xff0000);
graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
graphics.endFill( );
}
}
23
}
01
ye
in
ix
/l
et
.n
3.8. 隐藏Flash
dn
Flash
Flash播放器的菜单项
cs
/b !
g.
:/ 译
lo
问题
tp 翻
ht 青
我要隐藏右键菜单
常
解决办法
不能够完全改变Flash播放器的右键弹出菜单,但是可以设置stage.showDefaultContextMenu 属性
为false来最小化菜单项。
讨论
默认下Flash播放器右键弹出菜单的项目有:
Zoom In
Zoom Out
Show All
Quality (Low, Medium, or High)
Settings
Print
Show Redraw Regions (debug 版本)
Debugger (debug 版本)
57. About Adobe Flash Player 9
通过下面的语句可以移除许多项目,Settings和About是不能移除的:
stage.showDefaultContextMenu = false;
遗憾的是,Flash 没有提供方法完全禁用右键菜单。
3.9. 检测设备音频
问题
我要确定播放器正在使用的音频设备.
解决办法
23
使用 flash.system.Capabilities 类的 hasAudio 和 hasMP3 属性
01
ye
讨论
in
ix
如果用户系统有播放音频的能力,则 flash.system.Capabilities.hasAudio 属性就返回True 。这实
/l
际上很重要,如果目标设备不支持音频,那就要避免强制用户下载音频内容(因此音频内容都
et
.n
比较大)。
dn
// 只有当播放器可以播放声音才再如包含声音的.swf
cs
/b !
g.
if (flash.system.Capabilities.hasAudio) {
:/ 译
lo
tp 翻
content = "sound.swf";
ht 青
常
} else {
content = "silent.swf";
}
即时系统有播放音频能力也不意味着它有播放 mp3的能力。因此当发布含有mp3内容时应用
flash.system.Capabilities.hasMP3 属性检测下目标设备。
if (flash.system.Capabilities.hasMP3) {
var url:URLRequest = new URLRequest("sound.mp3");
sound = new Sound(url);
sound.play( );
} else {
// code to load an external .swf containing a ADCP sound
}
72. import ascb.util.NumberUtilities;
import flash.utils.Timer;
import flash.events.TimerEvent;
public class RandomNumberTest extends Sprite {
private var _total:uint;
private var _numbers:Object
public function RandomNumberTest( ) {
var timer:Timer = new Timer(10);
timer.addEventListener(TimerEvent.TIMER, randomizer);
timer.start( );
_total = 0;
_numbers = new Object( );
23
}
01
ye
private function randomizer(event:TimerEvent):void {
in
ix
var randomNumber:Number = NumberUtilities.random(1, 10, 1);
/l
et
_total++;
.n
if(_numbers[randomNumber] == undefined) {
dn
cs
_numbers[randomNumber] = 0;
/b !
g.
:/ 译
lo
}
tp 翻
ht 青
_numbers[randomNumber]++;
常
trace("random number: " + randomNumber);
var item:String;
for(item in _numbers) {
trace("t" + item + ": " + Math.round(100 * _numbers[item]/_total));
}
}
}
}
73. 4.8. 模拟硬币投掷
问题
我要模拟硬币投掷或布尔事件来达到50%几率成功。
解决办法
用NumberUtilities.random( ) 方法产生 0 到 1的整数,根据每种可能得出结果。
讨论
用 random( ) 方法产生指定范围的随机整数,能够产生两个结果对应硬币的正面和反面状态,
在程序里我们用0代表一个状态,1代表另一状态,当然你用1和2也是可以的,总之是2个 状 态 ,
这样就能模拟硬币投掷了:
package {
import flash.display.Sprite;
23
import flash.text.TextField;
01
ye
import flash.events.MouseEvent;
import ascb.util.NumberUtilities;
in
ix
/l
public class CoinExample extends Sprite {
et
.n
private var _field:TextField;
dn
cs
public function CoinExample( ) {
/b !
g.
:/ 译
lo
_field = new TextField( );
tp 翻
ht 青
_field.autoSize = "left";
常
addChild(_field);
var circle:Sprite = new Sprite( );
circle.graphics.beginFill(0, 100);
circle.graphics.drawCircle(100, 100, 100);
circle.graphics.endFill( );
circle.addEventListener(MouseEvent.CLICK, onClick);
addChild(circle);
}
private function onClick(event:MouseEvent):void {
var randomNumber:Number = NumberUtilities.random(0, 1);
_field.text = (randomNumber == 0) ? "heads" : "tails";
}
74. }
}
记录正反面出现次数:
package {
import flash.display.Sprite;
import flash.text.TextField;
import ascb.util.NumberUtilities;
public class CoinTest extends Sprite {
private var _field:TextField;
public function CoinTest( ) {
_field = new TextField( );
_field.autoSize = "left";
23
addChild(_field);
01
ye
var heads:Number = 0;
in
ix
var tails:Number = 0;
/l
et
var randomNumber:Number;
.n
for(var i:Number = 0; i < 10000; i++) {
dn
cs
randomNumber = NumberUtilities.random(0, 1);
/b !
g.
:/ 译
lo
if(randomNumber == 0) {
tp 翻
ht 青
heads++;
常
}
else {
tails++;
}
}
_field.text = "heads: " + heads + ", tails: " + tails;
}
}
}
如果要测试随机数,应该保存在变量中,像上面的代码,下面代码的统计是错误的,因此else if
每次都产生新的随机数:
package {
75. import flash.display.Sprite;
import ascb.util.NumberUtilities;
public class RandomLetter extends Sprite {
public function RandomLetter( ) {
for(var i:Number = 0; i < 10000; i++) {
trace(getRandomLetter( ));
}
}
private function getRandomLetter( ):String {
if(NumberUtilities.random(0, 2) == 0) {
return "A";
}
23
else if(NumberUtilities.random(0, 2) == 1) {
01
ye
return "B";
in
ix
}
/l
et
else if(NumberUtilities.random(0, 2) == 2) {
.n
return "C";
dn
cs
}
/b !
g.
:/ 译
lo
// It's possible that none of the preceding will evaluate to true,
tp 翻
ht 青
// and the method will reach this point without returning a valid
常
// string.
return "";
}
}
}
下面才正确:
package {
import flash.display.Sprite;
import ascb.util.NumberUtilities;
public class RandomLetter extends Sprite {
public function RandomLetter( ) {
76. for(var i:uint = 0; i < 10000; i++) {
trace(getRandomLetter( ));
}
}
private function getRandomLetter( ):String {
// random( ) 返回结果到变量上
var randomInteger:uint = NumberUtilities.random(0, 2);
if(randomInteger == 0) {
return "A";
}
else if(randomInteger == 1) {
return "B";
23
}
01
ye
else if(randomInteger == 2) {
in
ix
return "C";
/l
et
}
.n
return "";
dn
cs
}
/b !
g.
:/ 译
lo
}
tp 翻
ht 青
}
常
77. 4.9. 模拟骰子
问题
我要模仿掷骰子
解决办法
用NumberUtilities.random( ) 方法产生指定范围的随机数
讨论
用 random( ) 方法产生整数来模拟掷骰子,这在很多游戏中经常用到,这次我们在ActionScript
中实现
一般我们产生随机数然后保存它在使用,如果要重新使用存在的随机数,应保存它而不是再产
生新的随机数。注意下面两种情况,第一种,dice总是die1和die2的之和:
var die1:uint = NumberUtilities.random(1, 6);
23
var die2:uint = NumberUtilities.random(1, 6);
01
ye
var dice:uint = die1 + die2;
下面的情况,dice和die1和die2没有关系,换句话说,即使die1和die2加起来等于7,dice也不会
in
ix
/l
等于它:
et
.n
var die1:uint = NumberUtilities.random(1, 6);
dn
var die2:uint = NumberUtilities.random(1, 6);
cs
/b !
g.
var dice:uint = NumberUtilities.random(1, 6) + NumberUtilities.random(1, 6);
:/ 译
lo
tp 翻
NumberUtilities.random( ) 还可以模拟多边的骰子:
ht 青
常
var die1:uint = NumberUtilities.random(1, 15);
下面的例子模拟了一个骰子
package {
import flash.display.Sprite;
import flash.text.TextField;
import flash.events.MouseEvent;
import ascb.util.NumberUtilities;
public class NumbersAndMath extends Sprite {
var _die:Sprite;
var _value:uint;
public function NumbersAndMath( ) {
_die = new Sprite( );
84. 5.2. 遍历数组成员
问题
我要访问数组的每个元素
解决办法
利用 for 循环来遍历数组,使用下标返回元素。
讨论
for循环的初始变量从0开始,结束为array.length-1,因为是从下标0开始的:
var letters:Array = ["a", "b", "c"];
for (var i:int = 0; i < letters.length; i++) {
trace("Element " + i + ": " + letters[i]);
}
23
01
也可以降序遍历数组,循环变量从array.length-1开始到0:
ye
var letters:Array = ["a", "b", "c"];
in
ix
for (var i:int = letters.length - 1; i >= 0; i--){
/l
et
trace("Element " + i + ": " + letters[i]);
.n
dn
}
cs
有很多情况需要用循环遍历所有元素,比如,获得了包含sprite的数组, 然后把每个sprite的x坐
/b !
g.
:/ 译
lo
标+1:
tp 翻
ht 青
for (var i:int = 0; i < sprites.length; i++){
常
sprites[i].x++;
}
可以把数组长度存在变量中,免得每次循环都要重新计算:
var length:int = sprites.length;
for (var i:int = 0; i < length; i++){
sprites[i].x++;
}
这样做可以提高 Flash 性能,因为不用每次循环都去计算长度了,但是有个前提,就是没有进
行插入删除操作来改变长度值,否则就要每次计算长度才行。
85. 5.3. 搜索匹配的数组元素
问题
我要找出指定值得数组元素
解决办法
用 for 语 句 和 break 语 句 就 能 找 到 匹 配 的 元 素 。 另 外 用 ArrayUtilities.findMatchIndex( ),
ArrayUtilities.findLastMatchIndex( ), 和ArrayUtilities.findMatchIndices( ) 方法
讨论
用for循环查找第一个匹配的元素后,用break立即返回,这样就实现功能了。
break应该在if语句里进行判断,是否找到匹配元素,找到则执行break推出循环,否则继续查找。
var letters:Array = ["a", "b", "c", "d", "a", "b", "c", "d"];
// 指定要搜索的内容
23
var match:String = "b";
01
ye
for (var i:int = 0; i < letters.length; i++) {
in
ix
// 检测当前元素是否匹配
/l
et
if (letters[i] == match) {
.n
dn
trace("Element with index " + i +
cs
" found to match " + match);
/b !
g.
:/ 译
lo
break;
tp 翻
ht 青
}
常
}
也可以找到匹配的最后一个元素,这就需要倒序遍历数组:
var letters:Array = ["a", "b", "c", "d", "a", "b", "c", "d"];
var match:String = "b";
for (var i:int = letters.length - 1; i >= 0; i--) {
if (letters[i] == match) {
trace("Element with index " + i +
" found to match " + match);
break;
}
}
86. 使用自定义类ArrayUtilities 类更简单,它在 ascb.util 包中,首先导入它:
import ascb.util.ArrayUtilities;
ArrayUtilities 类 有 三 个 方 法 来 查 找 匹 配 的 元 素 findMatchIndex( ), findLastMatchIndex( ), 和
findMatchIndices( )。 findMatchIndex( ) 方法至少需要两个参数:一个指向数组的引用和需要匹
配的值,返回第一个匹配的元素下标,如果找不到返回-1:
var letters:Array = ["a", "b", "c", "d"];
trace(ArrayUtilities.findMatchIndex(letters, "b"));
// 显示: 1
trace(ArrayUtilities.findMatchIndex(letters, "r"));
// 显示: -1
也可以指定搜索的起始下标作为第三个参数:
var letters:Array = ["a", "b", "c", "d", "a", "b", "c", "d"];
trace(ArrayUtilities.findMatchIndex(letters, "a", 1));
23
01
// 显示: 4
ye
如果第三个参数为true,则返回部分匹配的元素: in
ix
/l
var words:Array = ["bicycle", "baseball", "mat", "board"];
et
.n
trace(ArrayUtilities.findMatchIndex(words, "s", true));
dn
// 显示: 1
cs
/b !
g.
如果你想部分匹配又想指定起始搜索下标,可以把起始下标作为第四个参数。
:/ 译
lo
tp 翻
findLastMatchIndex( ) 方法返回最后一个匹配的元素下标
ht 青
常
findMatchIndices( ) 方法返回所有匹配的元素下标数组:
var letters:Array = ["a", "b", "c", "d", "a", "b", "c", "d"];
trace(ArrayUtilities.findMatchIndices(letters, "b"));
// 显示: 1,5
也可以设定为部分匹配,指定第三个参数为true:
var words:Array = ["bicycle", "baseball", "mat", "board"];
trace(ArrayUtilities.findMatchIndices(words, "b", true));
// 显示: 0,1,3
ArrayUtilities 方法内部也是用for循环来实现的,现在我们看看代码,下面是findMatchIndex( ) 方
法的代码:
public static function findMatchIndex(array:Array, element:Object):int {
// Use a variable to determine the index
87. // from which to start. Use a default value of 0.
var startingIndex:int = 0;
// By default don't allow a partial match.
var partialMatch:Boolean = false;
// If the third parameter is a number,
// assign it to nStartingIndex.
// Otherwise, if the fourth parameter is a number,
// assign it to nStartingIndex instead.
if(typeof arguments[2] == "number") {
startingIndex = arguments[2];
}
else if(typeof arguments[3] == "number") {
23
startingIndex = arguments[3];
01
ye
}
in
ix
// If the third parameter is a Boolean value,
/l
et
// assign it to partialMatch.
.n
if(typeof arguments[2] == "boolean") {
dn
cs
partialMatch = arguments[2];
/b !
g.
:/ 译
lo
}
tp 翻
ht 青
// Assume no match is found.
常
var match:Boolean = false;
// Loop through each of the elements of the array
// starting at the specified starting index.
for(var i:int = startingIndex;
i < array.length; i++) {
// Check to see if the element either matches
// or partially matches.
if(partialMatch) {
match = (array[i].indexOf(element) != -1);
}
else {
88. match = (array[i] == element);
}
// If the element matches, return the index.
if(match) {
return i;
}
}
// The following return statement is only reached
// if no match was found. In that case, return -1.
return -1;
}
public static function findMatchIndices(array:Array,
23
element:Object, partialMatch:Boolean = false):Array {
01
ye
var indices:Array = new Array( );
in
ix
var index:int = findMatchIndex(array,
/l
et
element,
.n
partialMatch);
dn
cs
while(index != -1) {
/b !
g.
:/ 译
lo
indices.push(index);
tp 翻
ht 青
index = findMatchIndex(array,
常
element,
partialMatch,
index + 1);
}
return indices;
}
89. 5.4. 删除数组元素
问题
我要删除一个或多个数组元素,或移动数组元素
解决办法
splice( ) 方法删除指定位置的元素,pop( ) 删除尾部元素,shift( ) 删除首部元素
讨论
删除指定位置的元素使用splice( ) 方法,它需要两个参数:
start
开始下标
deleteCount
删除的元素个数,如果没有定义,则从起始位置到末尾全部删除:
23
01
var letters:Array = ["a", "b", "c", "d"];
ye
//从下标1开始删除1个元素 in
ix
letters.splice(1, 1);
/l
et
// 显示结果,现在只剩三个
.n
dn
// "a", "c", 和 "d".
cs
/b !
g.
for (var i:int = 0; i < letters.length; i++) {
:/ 译
lo
tp 翻
trace(letters [i]);
ht 青
常
}
splice( ) 方法也返回一个新的包含删除的元素数组:
var letters:Array = ["a", "b", "c", "d"];
//删除两个元素,从0位置开始
var deleted:Array = letters.splice(0, 2);
// 显示: "a","b".
for (var i:int = 0; i < deleted.length; i++) {
trace(deleted[i]);
}
删除首部和尾部的元素可用shift( ) 和pop( ) 方 法。 shift( ) 方法删除首部第一个元素,然后返回
该元素,pop( ) 方法删除尾部的元素并返回该值:
var letters:Array = ["a", "b", "c", "d"];
90. trace(letters.shift( ));
trace(letters.pop( ));
//显示剩下的元素
for (var i = 0; i < letters.length; i++) {
trace(letters[i]);
}
在for循环里删除原始,也要修改下标值,下面的代码演示不更新下标值变量出现的情况:
var numbers:Array = new Array(4, 10);
numbers[4] = 1;
trace(numbers); // 显示: 4,10,undefined,undefined,1
for(var i:int = 0; i < numbers.length; i++) {
if(numbers[i] == undefined) {
23
numbers.splice(i, 1);
01
ye
}
in
ix
}
/l
et
trace(numbers); // 显示: 4,10,undefined,1
.n
上面的代码本来是期望上出全部undefined 元素的,结果只删除一个,调试运行,看看发生了什
dn
cs
么:
/b !
g.
1。前两个循环什么都没做,因为都不是undefined.
:/ 译
lo
tp 翻
2。第三次找到 undefined 然后删除它,这时,第4个和第5个元素下移变成了第3个和第4个元
ht 青
素
常
3。 下一循环检测第4个元素, 也就是最后一个,这时忽略了第3个元素也就是那个undefined 元
素,因此,当删除元素,应该把小标变量-1,代码应该这样:
var numbers:Array = new Array(4, 10);
numbers[4] = 1;
trace(numbers); // 显示: 4,10,undefined,undefined,1
for(var i:int = 0; i < numbers.length; i++) {
if(numbers[i] == undefined) {
numbers.splice(i, 1);
i--;
}
}
trace(numbers); // 显示: 4,10,1
91. 5.5. 在数组中间插入元素
问题
我要在数组中间插入元素u
解决办法
使用splice( ) 方法
讨论
splice( ) 方法不仅可以删除元素,也可以插入元素,插入的元素放到第 2个参数之后,当第2个
参数为0代表插入元素:
var letters:Array = ["a", "b", "c", "d"];
//插入三个元素,起始位置为1
letters.splice(1, 0, "r", "s", "t");
23
// letters 现在包含的元素有:
01
ye
// "a", "r", "s", "t", "b", "c", "d".
in
ix
for (var i:int = 0; i < letters.length; i++) {
/l
et
trace(letters[i]);
.n
dn
}
cs
你也可以删除和插入同时执行:
/b !
g.
:/ 译
lo
var letters:Array = ["a", "b", "c", "d"];
tp 翻
ht 青
//删除2个,插入3个
常
letters.splice(1, 2, "r", "s", "t");
// myArray 现在的元素
// "a", "r", "s", "t", and "d".
for (var i:int = 0; i < letters.length; i++) {
trace(letters[i]);
}
92. 5.6. 转换字符串为数组
问题
我有一堆字符串,想把它转换为数组。
解决办法
使用String.split( ) 方法
讨论
String 类的 split( ) 方法把字符串转换为数组,但前提是字符串中含有统一的分割符,比如
Susan,Robert,Paula 字符串分割符为逗号
split( ) 方法接受两个参数:
分割符
用分割符来分割字符串,如果没定义,则把整个字符串作为数组的第一个元素
23
数量
01
ye
分割出的最大元素个数,如果没定义,则全部放入数组。
in
ix
可以使用空格符作为分割符:
/l
et
var list:String = "Peter Piper picked a peck of pickled peppers";
.n
dn
var words:Array = list.split(" ");
cs
split( ) 方法在用URLLoader 对象读取数据时经常用到,比如你接受服务器的一些姓名字符串:
/b !
g.
:/ 译
lo
names=Michael,Peter,Linda,Gerome,Catherine
tp 翻
ht 青
这是用split( ) 方法转换为数组:
常
// URLLoader 读取数据
var namesData:String = _loader.data;
var names:Array = namesData.split(",");
93. 5.7. 转换数组为字符串
问题
我要把数组转换为字符串
解决办法
使用 join( ) 方法
讨论
ActionScript 提供内建的方法 join( ) 可以快速把数组转换为字符串(数组中的元素不管什么类
型都将转换为字符串) ,该方法接受个参数作为分隔符:
var letters:Array = ["a", "b", "c"];
trace(letters.join("|")); // 显示: a|b|c
如果不指定分隔符,默认为逗号:
23
var letters:Array = ["a", "b", "c"];
01
ye
trace(letters.join()); // 显示: a,b,c
in
ix
/l
当 join( ) 的分隔符为逗号,其效果和toString( ) 一样。实际上当我们直接输出数组时系统就是
et
调用toString( ) 方法进行转换的,例如:
.n
dn
var letters:Array = ["a", "b", "c"];
cs
/b !
g.
trace(letters); // 显示: a,b,c
:/ 译
lo
tp 翻
ht 青
常
103. // 对两个字段排序
cars.sortOn(["year", "make"]);
for (var i:int = 0; i < cars.length; i++) {
/* 显示:
gold 1968 Chrysler
yellow 1975 Fiat
green 1975 Mercedes
black 1983 Fiat
gray 1983 Fiat
blue 1985 Mercedes
silver 1992 Honda
maroon 1997 Honda
23
beige 2000 Chrysler
01
ye
white 2000 Mercedes
in
ix
blue 2001 Honda
/l
et
orange 2004 Chrysler
.n
*/
dn
cs
trace(cars[i].color + "t" +
/b !
g.
:/ 译
lo
cars[i].year + "t" +
tp 翻
ht 青
cars[i].make);
常
}
下面的例子,先对 make, 再对year排序:
cars.sortOn(["make", "year"]);
for (var i:int = 0; i < cars.length; i++) {
/* 显示:
gold 1968 Chrysler
beige 2000 Chrysler
orange 2004 Chrysler
yellow 1975 Fiat
black 1983 Fiat
gray 1983 Fiat
104. silver 1992 Honda
maroon 1997 Honda
blue 2001 Honda
green 1975 Mercedes
blue 1985 Mercedes
white 2000 Mercedes
*/
trace(cars[i].color + "t" +
cars[i].year + "t" +
cars[i].make);
}
sortOn( ) 方法也可用那些数组常量完成降序,忽略大小写等排序:
23
cars.sortOn("year", Array.DESCENDING);
01
ye
for (var i:int = 0; i < cars.length; i++) {
in
ix
/* 显示:
/l
et
beige 2000 Chrysler
.n
maroon 1997 Honda
dn
cs
blue 1985 Mercedes
/b !
g.
:/ 译
lo
gray 1983 Fiat
tp 翻
ht 青
*/
常
trace(cars[i].color + "t" +
cars[i].year + "t" +
cars[i].make);
}
105. 5.11. 实现自定义排序
问题
我要自定义数组排序
解决办法
把自定义比较的函数引用传递给sort( ) 方法
讨论
如果要自定义排序,可用sort( ) 方法和自定义比较函数。sort( ) 方法重复调用比较函数对两个
数组元素进行比较,比较函数接受两个参数即数组元素(我们称为a和b) ,根据具体的排序方式
返回正数,负数或0。如果返回负数,a排在b前,如果返回0,位置不变,如果返回正数,a排在
b后,直到所有元素对比完毕。
下面有个例子对字符串数组进行自定义排序,比如是一个歌曲名数组,在排序时忽略字符串中
含有的"The" 字母,首先看看默认的排序:
23
01
var bands:Array = ["The Clash",
ye
"The Who", in
ix
"Led Zeppelin",
/l
et
"The Beatles",
.n
dn
"Aerosmith",
cs
/b !
g.
"Cream"];
:/ 译
lo
tp 翻
bands.sort( );
ht 青
常
for(var i:int = 0; i < bands.length; i++) {
trace(bands[i]);
/* 输出:
Aerosmith
Cream
Led Zeppelin
The Beatles
The Clash
The Who
*/
}
给 sort( ) 方法传递bandNameSort 比较函数:
106. var bands:Array = ["The Clash",
"The Who",
"Led Zeppelin",
"The Beatles",
"Aerosmith",
"Cream"];
bands.sort(bandNameSort);
for(var i:int = 0; i < bands.length; i++) {
trace(bands[i]);
/*输出
Aerosmith
The Beatles
23
The Clash
01
ye
Cream
in
ix
Led Zeppelin
/l
et
The Who
.n
*/
dn
cs
}
/b !
g.
:/ 译
lo
function bandNameSort(band1:String, band2:String):int
tp 翻
ht 青
{
常
band1 = band1.toLowerCase( );
band2 = band2.toLowerCase( );
if(band1.substr(0, 4) == "the ") {
band1 = band1.substr(4);
}
if(band2.substr(0, 4) == "the ") {
band2 = band2.substr(4);
}
if(band1 < band2) {
return -1;
}
107. else {
return 1;
}
}
bandNameSort( ) 函数把字符串元素转换为小写,然后检测是否含有"The ",如果有则剪切掉,
取剩余字符串进行比较
5.12. 数组元素的随机排序
问题
我要打乱数组元素的顺序
解决办法
使用 sort( ) 方法和自定义比较函数返回随机的正数或负数
23
01
讨论
ye
很多情况我们需要得到一个随机排列的数组,比如有个游戏需要产生随机的字母。 in
ix
/l
有很多种方法达到这个目的,但是最简单的办法就是创建自定义比较函数,返回随机的正数或
et
负数,把该函数引用传递给sort( ) 方法:
.n
dn
下面的比较函数就能达到目的:
cs
/b !
g.
function randomSort(elementA:Object, elementB:Object):Number {
:/ 译
lo
tp 翻
return Math.random( ) - .5
ht 青
常
}
Math.random( ) 返回0.0 到 1.0. 减去0.5 ,正好有一半的几率是负数,一半为正数,因此这个
数组经过随机排序
看下面的随机排序例子:
var numbers:Array = new Array( );
for(var i:int=0;i<20;i++) {
numbers[i] = i;
}
numbers.sort(randomSort);
for(var i:int=0;i<numbers.length;i++) {
trace(numbers[i]);
}
109. 5.14. 比较数组
问题
我该怎么知道两个数组是否相等呢
解决办法
循环数组,一一比较对应位置的每个元素
讨论
因为数组是引用类型,使用=操作符只能对比引用是否指向同一内存空间,如:
var letters:Array = ["a", "b", "c", "d"];
var lettersPointer:Array = letters;
trace(letters == lettersPointer); // 显示: true
但是如果数组内容相同,但是在不同的内存空间,=操作就会返回false:
23
01
var letters1:Array = ["a", "b", "c", "d"];
ye
var letters2:Array = ["a", "b", "c", "d"]; in
ix
trace(letters1 == letters2]; // 显示: false
/l
et
因此,比较数组应该比较数组的每个元素是否相等:
.n
dn
var equivalent:Boolean = true;
cs
/b !
g.
for(var i:int = 0; i < letters1.length; i++) {
:/ 译
lo
tp 翻
if(letters1[i] != letters2[i]) {
ht 青
常
equivalent = false;
break;
}
}
trace(equivalent); // 显示: true
另外还可以用ArrayUtilities.equals( ) 方法,该方法需要两个参数:两个数组引用,返回布尔值
说明是否相等:
var letters1:Array = ["a", "b", "c", "d"];
var letters2:Array = ["a", "b", "c", "d"];
trace(ArrayUtilities.equals(letters1, letters2)); // 显示: true
默认,两个不同排列的数组是不相等的,除非提供第3个参数为true表示忽略数组排列顺序:
var letters1:Array = ["a", "b", "c", "d"];
110. var letters2:Array = ["b", "a", "d", "c"];
trace(ArrayUtilities.equals(letters1, letters2)); // 显示: false
trace(ArrayUtilities.equals(letters1, letters2, true)); // 显示: true
equals( ) 方法用起来很简单,下面是它的代码:
public static function equals(arrayA:Array, arrayB:Array,
bNotOrdered:Boolean):Boolean {
// 如果两个数组长度不同
if(arrayA.length != arrayB.length) {
return false;
}
// 创建拷贝,不影响原数组
var arrayACopy:Array = arrayA.concat( );
23
var arrayBCopy:Array = arrayB.concat( );
01
ye
// 如果忽略排列顺序
in
ix
if(bNotOrdered) {
/l
et
arrayACopy.sort( );
.n
arrayBCopy.sort( );
dn
cs
}
/b !
g.
:/ 译
lo
// 循环比较,如果不匹配,删除拷贝,返回false
tp 翻
ht 青
for(var i:int = 0; i < arrayACopy.length; i++) {
常
if(arrayACopy[i] != arrayBCopy[i]) {
delete arrayACopy;
delete arrayBCopy;
return false;
}
}
// 否则相等,删除数组,返回true
delete arrayACopy;
delete arrayBCopy;
return true;
}
111. 5.15. 创建关联数组
问题
我要创建用名称元素作为索引的数组
解决办法
创建关联数组
讨论
用关联数组其每个元素都有特定的含义,这一点原来的数组类型是做不到的。
var aMembers:Array = new Array("Franklin", "Gina", "Sindhu");
关联数组在其他的语言叫做哈希表,在 ActionScript 里它就是Object 类的一个实例,关联数组
使用名称元素来代替数字下标,该名称也被称为关键字或属性,说关键字更好理解些,它关联
了元素值,两者一一对应。
23
创建关联数组不是用 Array 类而是Object类创建的,它就是 Object 类的一个实例,理论上
01
Object 类是任何类的基类。所有的对象都能作为关联数组,但是除非有特殊需要,最好还是用
ye
Object 类创建。 in
ix
用{ } ,而且用逗号分开每个键值对,键值对之间用:,像下面:
/l
et
var memebers:Object = {scribe: "Franklin",
.n
dn
chairperson: "Gina",
cs
/b !
g.
treasurer: "Sindhu"};
:/ 译
lo
tp 翻
也可以像下面那样创建关联数组:
ht 青
常
var members:Object = new Object( );
members.scribe = "Franklin";
members.chairperson = "Gina";
members.treasurer = "Sindhu";
有两种方法访问关联数组内容,一种是通过访问属性名称(关键字):
trace(members.scribe); // 显示: Franklin
另一种就像数组那样,把关键字作为下标来访问,用[ ] 符号:
trace(members["scribe"]); // 显示: Franklin
这种方式更加灵活,可以在数组中进行遍历,对于动态生成的关键值和内容这种访问方式是最
好的,例如:
var members:Object = new Object();
members.councilperson1 = "Beatrice";
112. members.councilperson2 = "Danny";
members.councilperson3 = "Vladamir";
for (var i:int = 1; i <= 3; i++) {
trace(members["councilperson" + i];
}
数组访问方式在循环语句里经常用到:
var members:Object = new Object( );
members["councilperson"] = "Ruthie";
trace(members.councilperson); // 显示 Ruthie
members.councilperson = "Rebecca";
trace(members["councilperson"]); // 显示: Rebecca
23
01
ye
in
ix
/l
et
.n
dn
cs
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
113. 5.16. 读取关联数组
问题
我要怎样遍历关联数组
解决办法
使用 for . . . in 语句
讨论
基于整形下标的数组可以通过 for 语句进行循环遍历,但是,用关键字作索引的关联数组就不
能这样遍历了,还好,关联数组可以通过 for . . . in 语句进行遍历访问。该语句会访问指定对
象所有可用的属性,语法如下:
for (key in object) {
// Actions
23
}
01
for . . . in 语句不需要循环变量更新语句,决定循环次数的是对象的属性个数。注意这key 就是
ye
存储每个属性名称的: in
ix
/l
var members:Object = new Object( );
et
.n
members.scribe = "Franklin";
dn
members.chairperson = "Gina";
cs
/b !
g.
members.treasurer = "Sindhu";
:/ 译
lo
tp 翻
// 使用 for . . . in 语句遍历所有元素
ht 青
常
for (var sRole:String in members) {
// 显示:
// treasurer: Sindhu
// chairperson: Gina
// scribe: Franklin
trace(sRole + ": " + members[sRole]);
}
124. addChild( blue );
// 把蓝色圆移到最底层
setChildIndex( blue, 0 );
}
public function createCircle( color:uint, radius:Number ):Shape {
var shape:Shape = new Shape( );
shape.graphics.beginFill( color );
shape.graphics.drawCircle( 0, 0, radius );
shape.graphics.endFill( );
return shape;
}
}
23
}
01
ye
setChildIndex( ) 方法需要明确知道把子对象移到哪里,把它移到最底层就指定为0,把它移动到
最顶层就指定为numChildren-1,但如果我要移动到某个对象之后呢?
in
ix
/l
举个例子,假设我有两个圆,一个绿色一个蓝色,事先我不知道他们的位置,现在我要把蓝色
et
的移动到绿色的前面,setChildIndex( ) 就做不到了,解决的办法就是用getChildIndex( )方法 获
.n
dn
得对象的位置,然后用setChildIndex( )方 法设 置,getChildIndex( ) 方法只能返回队列中对象位置 ,
cs
否则就会抛出ArgumentError 异常
/b !
g.
:/ 译
lo
下面的例子创建了两个圆,绿色和蓝色的,getChildIndex( )得到绿色圆的位置来设置蓝色圆的位
tp 翻
置:
ht 青
常
package {
import flash.display.*;
public class GetChildIndexExample extends Sprite {
public function GetChildIndexExample( ) {
// 创建圆
var green:Shape = createCircle( 0x00FF00, 10 );
green.x = 25;
green.y = 25;
var blue:Shape = createCircle( 0x0000FF, 20 );
blue.x = 25;
blue.y = 25;
addChild( green );
125. addChild( blue );
// 把蓝色圆放在绿色圆的后面
setChildIndex( blue, getChildIndex( green ) );
}
public function createCircle( color:uint, radius:Number ):Shape {
var shape:Shape = new Shape( );
shape.graphics.beginFill( color );
shape.graphics.drawCircle( 0, 0, radius );
shape.graphics.endFill( );
return shape;
}
}
23
}
01
ye
如果对象a在b前,下面的代码直接把a移到b后面:
in
ix
setChildIndex( a, getChildIndex( b ) );
/l
et
如果a在b后面,上面的代码就把a移到b前面了。
.n
如果事先没获得子对象引用的话就只能用getChildAt( ) 方法了。
dn
cs
getChildAt( ) 方法需要一个参数既列表子对象位置,返回该对象引用,如果给出的位置不合法
/b !
g.
:/ 译
lo
则抛出RangeError 异常。
tp 翻
下面的例子创建了多个各种颜色,不同大小不同位置的圆,每次鼠标点击最底层的子对象就会
ht 青
常
移到最上层:
package {
import flash.display.*;
import flash.events.*;
public class GetChildAtExample extends Sprite {
public function GetChildAtExample( ) {
var color:Array = [ 0xFF0000, 0x990000, 0x660000, 0x00FF00,
0x009900, 0x006600, 0x0000FF, 0x000099,
0x000066, 0xCCCCCC ];
for ( var i:int = 0; i < 10; i++ ) {
var circle:Shape = createCircle( color[i], 10 );
circle.x = i;
126. circle.y = i + 10; // the + 10 adds padding from the top
addChild( circle );
}
stage.addEventListener( MouseEvent.CLICK, updateDisplay );
}
public function updateDisplay( event:MouseEvent ):void {
setChildIndex( getChildAt(0), numChildren - 1 );
}
public function createCircle( color:uint, radius:Number ):Shape {
var shape:Shape = new Shape( );
shape.graphics.beginFill( color );
shape.graphics.drawCircle( 0, 0, radius );
23
shape.graphics.endFill( );
01
ye
return shape;
in
ix
}
/l
et
}
.n
}
dn
cs
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
127. 6.4. 创建自定义可视化类
问题
我要创建新的可视化类
解决办法
继承DisplayObject 或它的子类来创建新类
讨论
基于新的可视化对象列表模型创建新的类是非常方便的。这在过去,只有继承 MovieClip 才能
创建新的类,现在完全不同了,新的可视化模型处理起来更简单,用ActionScript代码可以做更
多的事情了。
新的可视化模型,正如介绍里讨论过的那样,现在除了MovieClip.还有很多可视化类可用。在你
创建新类前, 你需要决定的是创建什么类型的类。比如说你要创建一个图形类, 这就要继承Shape
类,如果你要创建自定义按钮,你可能要继承SimpleButton类。如果你要创建一个容纳其他对象
23
的容器, Sprite 就是个很好的继承类,它不需要使用时间线,如果你需要时间线,需要继承
01
ye
MovieClip。
所有的可视化类都有其特定用途,当你创建类时首先要决定新的类是什么用途,然后选择相应
in
ix
/l
的基类继承之。选择什么做父类也是很讲究的,不然会造成性能下降和资源浪费。比如一个简
et
单的Circle 类不需要继承自MovieClip , 该 不 需 要 时 间 线 ,
应 Shape 类才是作为父类的最后选择 。
.n
dn
基类写好了,接着就该写代码了,看下面的例子:创建了一个Circle 类,继承自Shape 类,文
cs
件名为Circle.as:
/b !
g.
:/ 译
lo
package {
tp 翻
ht 青
import flash.display.Shape;
常
public class Circle extends Shape {
private var _color:uint;
private var _radius:Number;
public function Circle( color:uint = 0x000000, radius:Number = 10 ) {
_color = color;
_radius = radius;
draw( );
}
private function draw( ):void {
graphics.beginFill( _color );
graphics.drawCircle( 0, 0, _radius );
128. graphics.endFill( );
}
}
}
上面的代码定义了新的Circle 可视化类,创建Circle 实例时可以指定颜色和半径。
现在只要把 Circle 类实例用addChild( ) 或 addChildAt( ) 加到可视化对象列表中就可以在屏幕
上显示了。下面的代码例子创建了Circle 实例并显示在屏幕上:
package {
import flash.display.Sprite;
public class UsingCircleExample extends Sprite {
public function UsingCircleExample( ) {
var red:Circle = new Circle( 0xFF0000, 10 );
red.x = 10;
23
01
red.y = 20;
ye
var green:Circle = new Circle( 0x00FF00, 10 ); in
ix
/l
green.x = 15;
et
.n
green.y = 25;
dn
var blue:Circle = new Circle( 0x0000FF, 10 );
cs
/b !
g.
blue.x = 20;
:/ 译
lo
tp 翻
blue.y = 20;
ht 青
常
// Add the circles to the display list
addChild( red );
addChild( green );
addChild( blue );
}
}
}
129. 6.5. 创建简单的按钮
问题
我想创建一个交互式按钮,点击鼠标执行一段代码,比如发送表单或计算结果
解决办法
创建SimpleButton 类实例和创建upState, downState, overState, 和 hitTestState等对象。当用户点
击按钮时用click事件激活方法
讨论
可视化对象列表模型提供一种简单的方法通过 SimpleButton 类创建按钮。SimpleButton 类允许
用户用鼠标和可视化对象进行交互,通过各种状态定义交互方法,SimpleButton 按钮的状态有
以下这些:
upState
代表按钮默认"up"状态的对象,当鼠标不在按钮上,按钮就处于这个状态。
23
01
overState
ye
当鼠标移动到按钮上时按钮的状态,鼠标离开时按钮又回到"up"状态。 in
ix
/l
downState
et
当鼠标按下左键时所处的状态
.n
dn
hitTestState
cs
/b !
g.
这个状态定义按钮的界限,只是用来跟踪鼠标的目的。
:/ 译
lo
tp 翻
下面 的例子创建了一个 SimpleButton 实例 ,定义了四个状态属性。每个状态都需要一个
ht 青
DisplayObject 实例相关联,createCircle( ) 方法创建不同颜色的图形作为按钮状态:
常
package {
import flash.display.*;
import flash.events.*;
public class SimpleButtonDemo extends Sprite {
public function SimpleButtonDemo( ) {
var button:SimpleButton = new SimpleButton( );
button.x = 20;
button.y = 20;
button.upState = createCircle( 0x00FF00, 15 );
button.overState = createCircle( 0xFFFFFF, 16 );
button.downState = createCircle( 0xCCCCCC, 15 );
130. button.hitTestState = button.upState;
button.addEventListener( MouseEvent.CLICK, handleClick );
addChild( button );
}
private function createCircle( color:uint, radius:Number ):Shape {
var circle:Shape = new Shape( );
circle.graphics.lineStyle( 1, 0x000000 );
circle.graphics.beginFill( color );
circle.graphics.drawCircle( 0, 0, radius );
circle.graphics.endFill( );
return circle;
}
23
private function handleClick( event:MouseEvent ):void {
01
ye
trace( "Mouse clicked on the button" );
in
ix
}
/l
et
}
.n
}
dn
cs
运行上面的代码后,绿色圆显示在屏幕上。当你的鼠标移动到绿色圆上时,白色圆现实出来了,
/b !
g.
:/ 译
lo
点击这个白色圆,白色圆变成了灰色圆,这就是SimpleButton 实例通过不同的状态显示不同的
tp 翻
对象。
ht 青
常
addEventListener( ) 方法注册鼠标事件和处理函数,上面的代码注册了MouseEvent.CLICK事件,
也就是鼠标单击事件, 激活该事件后由handleClick( ) 方法处理,任何时候只要用户点击该按钮 ,
handleClick( ) 方法就被激活,在这里编写处理代码,这里只是简单的输出个字符串显示在控制
台上。
hitTestState 属性可能最有意思了,我们注意到上面的代码中把hitTestState 和upState设成了一样
的状态。也就说当鼠标进入upState 对象范围大小时就激活事件。
hitTestState 可以被设置成任何可显示的对象,像下面的代码那样把激活区域设大些:
button.hitTestState = createCircle( 0x000000, 50 );
再次运行你会发现,当鼠标还没靠近圆,over状态就激活了,这就是激活半径现在是50的缘故。
下面继承SimpleButton 创建可视化类,包括定义四个状态,命名为RectangleButton:
package {
import flash.display.*
import flash.text.*;
131. import flash.filters.DropShadowFilter;
public class RectangleButton extends SimpleButton {
// 显示在按钮上的文本
private var _text:String;
// 保存矩形的宽度和高度
private var _width:Number;
private var _height:Number;
public function RectangleButton( text:String, width:Number, height:Number ) {
// Save the values to use them to create the button states
_text = text;
_width = width;
_height = height;
23
// 创建按钮状态
01
ye
upState = createUpState( );
in
ix
overState = createOverState( );
/l
et
downState = createDownState( );
.n
hitTestState = upState;
dn
cs
}
/b !
g.
:/ 译
lo
// 创建状态对象
tp 翻
ht 青
private function createUpState( ):Sprite {
常
var sprite:Sprite = new Sprite( );
var background:Shape = createdColoredRectangle( 0x33FF66 );
var textField:TextField = createTextField( false );
sprite.addChild( background );
sprite.addChild( textField );
return sprite;
}
private function createOverState( ):Sprite {
var sprite:Sprite = new Sprite( );
var background:Shape = createdColoredRectangle( 0x70FF94 );
var textField:TextField = createTextField( false );
132. sprite.addChild( background );
sprite.addChild( textField );
return sprite;
}
private function createDownState( ):Sprite {
var sprite:Sprite = new Sprite( );
var background:Shape = createdColoredRectangle( 0xCCCCCC );
var textField:TextField = createTextField( true );
sprite.addChild( background );
sprite.addChild( textField );
return sprite;
}
23
private function createdColoredRectangle( color:uint ):Shape {
01
ye
var rect:Shape = new Shape( );
in
ix
rect.graphics.lineStyle( 1, 0x000000 );
/l
et
rect.graphics.beginFill( color );
.n
rect.graphics.drawRoundRect( 0, 0, _width, _height, 15 );
dn
cs
rect.graphics.endFill( );
/b !
g.
:/ 译
lo
rect.filters = [ new DropShadowFilter( 2 ) ];
tp 翻
ht 青
return rect;
常
}
// 创建按钮上的文字
private function createTextField( downState:Boolean ):TextField {
var textField:TextField = new TextField( );
textField.text = _text;
textField.width = _width;
var format:TextFormat = new TextFormat( );
format.align = TextFormatAlign.CENTER;
textField.setTextFormat( format );
//垂直居中
textField.y = ( _height - textField.textHeight ) / 2;
133. textField.y -= 2; // Subtract 2 pixels to adjust for offset
if ( downState ) {
textField.x += 1;
textField.y += 1;
}
}
}
}
现在已经完成了RectangleButton 类的封装设计,只需要调用构造函数就可以创建实例了,看下
面的代码演示:
package {
import flash.display.*;
23
public class SimpleButtonDemo extends Sprite {
01
ye
public function SimpleButtonDemo( ) {
// 创建三个不同文字不同大小和位置的矩形按钮
in
ix
/l
var button1:RectangleButton = new RectangleButton( "Button 1", 60, 100 );
et
.n
button1.x = 20;
dn
cs
button1.y = 20;
/b !
g.
:/ 译
lo
var button2:RectangleButton = new RectangleButton( "Button 2", 80, 30 );
tp 翻
ht 青
button2.x = 90;
常
button2.y = 20;
var button3:RectangleButton = new RectangleButton( "Button 3", 100, 40 );
button3.x = 100;
button3.y = 60;
addChild( button1 );
addChild( button2 );
addChild( button3 );
}
}
}
134. 6.6. 动态载入外部图片
问题
我要在Flash运行时动态载入图片
解决办法
使用新的Loader类载入图片(jpg,png,gif)
讨论
9.17节 将展示如何在编译期通过[Embed] 元数据标签绑定外部文件到Flash。在运行期间载入外
部图片或flash需要用到Loader 类。
flash.display.Loader 类非常类似于flash.net.URLLoader 类(19.3节讨论)。不 同 的 是 Loader 实例
能载入外部图片和flash,在传输数据方面URLLoader更有用些。
载入外部内容需要三个步骤:
23
创建Loader 类实例
01
把Loader 实例加到显示列表里
ye
调用load( )方法载入外部内容 in
ix
load( ) 方法下载图片或.swf文件,它需要一个URLRequest 对象作为参数,该对象指定一个需要
/l
et
下载资源的URL。
.n
下面的例子通过Loader 实例下载一张图片image.jpg :
dn
cs
package {
/b !
g.
:/ 译
lo
import flash.display.*;
tp 翻
ht 青
import flash.net.URLRequest;
常
public class LoaderExample extends Sprite {
public function LoaderExample( ) {
// 1. 创建Loader 类实例
var loader:Loader = new Loader( );
// 2. 添加到可视化对象列表
addChild( loader );
// 3. 调用load( )方法
loader.load( new URLRequest( "image.jpg" ) );
}
}
}
135. 当运行代码时,播放器寻找.swf 文件所在目录下的图片文件,URLRequest 对象使用相对URL,
也可以是绝对URL。如果下载成功会自动当作Loader 实例的子对象。
在载入外部资源的过程中可能会出现错误,比如,可能是URL地址不正确,或者是Flash播放器
安全沙漏不允许,或者资源太大需要很长的时间下载,需要一个进度条告诉用户下载进度。
面对这些可能的情况,我们需要添加一个事件去检测它, Loader实例的contentLoaderInfo 属性
会对不同的情况作出不同的反应事件。 contentLoaderInfo 属性是 flash.display.LoaderInfo 类实
例,用来提供目标被载入时的信息,下面是LoaderInfo 类的一些有用的事件:
open
当资源开始下载时触发
progress
资源在下载中时触发
complete
当资源下载完成时触发
23
init
01
ye
当载入外部的.swf初始化时触发
in
ix
httpStatus
/l
et
当载入外部资源的HTTP请求产生状态代码错误时触发
.n
dn
ioError
cs
当一个错误导致下载被终止时触发,比如找不到相应资源
/b !
g.
:/ 译
lo
securityError
tp 翻
ht 青
当试图读取安全沙漏以外的数据时触发
常
unload
当unload( ) 方法被调用或移除载入的内容时或再次调用load( ) 方法时都会触发该事件
下面的例子演示了contentLoaderInfo.的事件:
package {
import flash.display.*;
import flash.text.*;
import flash.net.URLRequest;
import flash.events.*;
public class LoaderExample extends Sprite {
public function LoaderExample( ) {
// Create the loader and add it to the display list
136. var loader:Loader = new Loader( );
addChild( loader );
loader.contentLoaderInfo.addEventListener( Event.OPEN, handleOpen );
loader.contentLoaderInfo.addEventListener( ProgressEvent.PROGRESS, handleProgress );
loader.contentLoaderInfo.addEventListener( Event.COMPLETE, handleComplete );
loader.load( new URLRequest( "image.jpg" ) );
}
private function handleOpen( event:Event ):void {
trace( "open" );
}
private function handleProgress( event:ProgressEvent ):void {
var percent:Number = event.bytesLoaded / event.bytesTotal * 100;
23
trace( "progress, percent = " + percent );
01
ye
}
in
ix
private function handleComplete( event:Event ):void {
/l
et
trace( "complete" );
.n
}
dn
cs
}
/b !
g.
:/ 译
lo
}
tp 翻
ht 青
运行上面的代码,在控制台上会显示下载百分比等信息。
常
下面我们修改下代码,在下载的过程中显示百分比。比如handleOpen( ) 方法创建一个显示百分
比的文本框,handleProgress( ) 更新文本框的百分比,最后handleComplete( ) 方法进行清除,因
为资源已全部下载:
private function handleOpen( event:Event ):void {
_loaderStatus = new TextField( );
addChild( loaderStatus );
_loaderStatus.text = "Loading: 0%";
}
private function handleProgress( event:ProgressEvent ):void {
var percent:Number = event.bytesLoaded / event.bytesTotal * 100;
_loaderStatus.text = "Loading: " + percent + "%";
}
137. private function handleComplete( event:Event ):void {
removeChild( loaderStatus );
_loaderStatus = null;
}
在类中添加_loaderStatus 变量,类型为TextField。
private var _loaderStatus:TextField;
6.7. 载入外部swf
swf
swf文件并与之交互
问题
我要载入而且控制外部的SWF文件
23
解决办法
01
使用Loader 类载入.swf 文件,然后通过Loader实例的content 属性访问
ye
讨论
in
ix
/l
6.6节 演示了如何通过Loader 类载入外部图片资源,载入swf 文件也是同样的方法,通过load( )
et
方法,然后传递一个.swf文件的URL。
.n
dn
我们创建两个独立的.swf文件: ExternalMovie.swf 和LoaderExample.swf。第一个稍后要被载入
cs
/b !
g.
到第二个上,也就是LoaderExample.swf。ExternalMovie.swf 的代码如下:
:/ 译
lo
tp 翻
package {
ht 青
常
import flash.display.Sprite;
import flash.display.Shape;
public class ExternalMovie extends Sprite {
private var _color:uint = 0x000000;
private var _circle:Shape;
public function ExternalMovie( ) {
updateDisplay( );
}
private function updateDisplay( ):void {
// 如果circle 没有创建则创建之并显示
if ( _circle == null ) {
_circle = new Shape( );
138. addChild( _circle );
}
// 清除以前画的内容重新画并填充之
_circle.graphics.clear( );
_circle.graphics.beginFill( _color );
_circle.graphics.drawCircle( 100, 100, 40 );
}
// 改变颜色
public function setColor( color:uint ):void {
_color = color;
updateDisplay( );
}
23
// 获取颜色
01
ye
public function getColor( ):uint {
in
ix
return _color;
/l
et
}
.n
}
dn
cs
}
/b !
g.
:/ 译
lo
ExternalMovie.swf 运行时就画了一个黑色的圆,需要注意的是有两个用来访问和设置颜色的方
tp 翻
法getColor( ) 和setColor( )。无论何时setColor( ) 方法被调用,圆立即被重画。
ht 青
常
申明方法为public,这样载入它的类也可以访问这些方法, updateDisplay( ) 方法是私有的,也
就是说LoaderExample.swf,是无法调用它的。
现在通过LoaderExample.swf去载入ExternalMovie.swf,代码如下:
package {
import flash.display.*;
import flash.net.URLRequest;
import flash.events.Event;
public class LoaderExample extends Sprite {
private var _loader:Loader;
public function LoaderExample( ) {
// 创建Loader并显示
_loader = new Loader( );
149. private var _green:DraggableSprite;
private var _blue:DraggableSprite;
private var _white:Sprite;
// Saves the starting coordinates of a dragging Sprite so
// it can be placed back
private var startingLocation:Point;
// Create the rectangles that comprise the interface
// and wire the mouse events to make them interactive
public function ColorDrop( ) {
createRectangles( );
addEventListeners( );
}
23
private function createRectangles( ):void {
01
ye
_red = new DraggableSprite( );
in
ix
_red.graphics.beginFill( 0xFF0000 );
/l
et
_red.graphics.drawRect( 0, 10, 10, 10 );
.n
_red.graphics.endFill( );
dn
cs
_green = new DraggableSprite( )
/b !
g.
:/ 译
lo
_green.graphics.beginFill( 0x00FF00 );
tp 翻
ht 青
_green.graphics.drawRect( 0, 30, 10, 10 );
常
_green.graphics.endFill( );
_blue = new DraggableSprite( );
_blue.graphics.beginFill( 0x0000FF );
_blue.graphics.drawRect( 0, 50, 10, 10 );
_blue.graphics.endFill( );
_white = new DraggableSprite( );
_white.graphics.beginFill( 0xFFFFFF );
_white.graphics.drawRect( 20, 10, 50, 50 );
_white.graphics.endFill( );
addChild( _red );
addChild( _green );
150. addChild( _blue );
addChild( _white );
}
private function addEventListeners( ):void {
_red.addEventListener( MouseEvent.MOUSE_DOWN, pickup );
_red.addEventListener( MouseEvent.MOUSE_UP, place );
_green.addEventListener( MouseEvent.MOUSE_DOWN, pickup );
_green.addEventListener( MouseEvent.MOUSE_UP, place );
_blue.addEventListener( MouseEvent.MOUSE_DOWN, pickup );
_blue.addEventListener( MouseEvent.MOUSE_UP, place );
}
public function pickup( event:MouseEvent ):void {
23
// Save the original location so you can put the target back
01
ye
startingLocation = new Point( );
in
ix
startingLocation.x = event.target.x;
/l
et
startingLocation.y = event.target.y;
.n
// Start dragging the Sprite that was clicked on and apply
dn
cs
// a drop shadow filter to give it depth
/b !
g.
:/ 译
lo
event.target.drag( );
tp 翻
ht 青
event.target.filters = [ new DropShadowFilter( ) ];
常
// Bring the target to front of the display list so
// that it appears on top of everything else
setChildIndex( DisplayObject( event.target ), numChildren - 1 );
}
public function place( event:MouseEvent ):void {
// Stop dragging the Sprite around and remove the depth
// effect from the filter
event.target.drop( );
event.target.filters = null;
// Get a list of objects inside this container that are
// underneath the mouse
151. var dropTargets:Array = getObjectsUnderPoint( new Point( mouseX, mouseY ) );
// The display object at position length - 1 is the top-most object,
// which is the rectangle that is currently being moved by the mouse.
// If the white rectangle is the one immedialy beneath that, the
// drop is valid
if ( dropTargets[ dropTargets.length - 2 ] == _white ) {
// Determine which color was dropped, and apply that color
// to the white rectangle
var color:uint;
switch ( event.target ) {
case _red: color = 0xFF0000; break;
case _green: color = 0x00FF00; break;
23
case _blue: color = 0x0000FF; break;
01
ye
}
in
ix
_white.graphics.clear( );
/l
et
_white.graphics.beginFill( color );
.n
_white.graphics.drawRect( 20, 10, 50, 50 );
dn
cs
_white.graphics.endFill( );
/b !
g.
:/ 译
lo
}
tp 翻
ht 青
// Place the dragging Sprite back to its original location
常
event.target.x = startingLocation.x;
event.target.y = startingLocation.y;
}
}
}
152. 7.0. 介绍
在ActionScript中,我们可以通过编程画出 Shape, Sprite, Button, 和 MovieClip 。每个类都有个
graphics 属性,它是flash.display.Graphics 类实例。Graphics 类定义了一些绘图内容的API。这
一章的讨论的基本上是如何使用Graphics类API。
因为 Shape, Sprite, Button, 和MovieClip 类已经定义了graphics 属性,它就是 Graphics实例的引
用,所以没有必要再构造新的Graphics 对象。对象的graphics 属性可在对象内绘图。比如下面
的代码设置了sprite的线条样式:
sampleSprite.graphics.lineStyle( );
Graphics 类定义了一些基本的绘图方法,如直线,简单的图形。但是大多数的图形用Graphics
API 还 是 很 难 画 出 的 , AS3CBLibrary (http://www.rightactionscript.com/ascb) 提 供 了 一 个
ascb.drawing.Pen 类。Pen 类是Graphics 类的代理(包装)类。你可以构造一个新的Pen 实例
然后传递进Graphics对象引用作为参数:
23
var pen:Pen = new Pen(sampleSprite.graphics);
01
ye
Pen 类代理了所有Graphics类的方法。这意味着Graphics的所有方法都可以在Pen 类中使用。另
外Pen 类还定义了一些API 能更简单的画出弧线,椭圆,多边形,星形等等。我们会在下面的 in
ix
章节中讨论Pen类。
/l
et
.n
dn
cs
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
166. 7.13. 用渐变色填充图形
问题
我要用渐变色填充图形
解决办法
使用beginGradientFill( )和endFill( )方法完成渐变填充
讨论
渐变色填充是用多种颜色之间的渐变层次来做填充色。Flash支持线形渐变,就是左边的颜色渐
变到右边的颜色(这是水平渐变,也可以垂直渐变) ,或者指定旋转角度。Flash也支持放射渐
变,它是从中心到四周渐变颜色。通过beginGradientFill( )启动渐变填充,它的参数有::
gradientType
flash.display.GradientType类常量,为LINEAR或RADIAL。
23
colors
01
RGB 值的数组
ye
alphas
in
ix
/l
透明度值数组
et
.n
ratios
dn
cs
根据颜色和透明度指定最后的纯度,范围为0到255。
/b !
g.
:/ 译
lo
matrix
tp 翻
flash.geom.Matrix 对象定义用于渐变的转换。默认为1x1的渐变转换来填充图形。Matrix类定义
ht 青
常
了createGradientBox( )方法,它接受以下参数:
scaleX
水平缩放比
scaleY
垂直缩放比
rotation
旋转角度,可以把角度转换为弧度,公式为Math.PI/180;默认为0
tx
x方向的转换数量,默认为0
ty
y方向的转换数量,默认为0
spreadMethod
167. flash.display.SpreadMethod 类常量,有PAD, REFLECT, 和REPEAT。默认为PAD。
interpolationMethod
flash.display.InterpolationMethod类常量,有LINEAR_RGB和RGB。默认为RGB。插补方法影响
颜色渐变。
focalPointRatio
改值范围从-1 到1 指示渐变的焦点(对线形渐变无效)。默认为0,也就是位于中点。
下面的例子画出一个渐变填充圆:
var matrix:Matrix = new Matrix( );
matrix.createGradientBox(100, 100, 0, 50, 50);
var colors:Array = [0xFF0000, 0x0000FF];
var alphas:Array = [100, 100];
var ratios:Array = [0x00, 0xFF];
23
sampleSprite.graphics.lineStyle( );
01
ye
sampleSprite.graphics.beginGradientFill(GradientType.GRADIENT, colors, alphas, ratios, matrix);
sampleSprite.graphics.drawCircle(100, 100, 50);
in
ix
/l
sampleSprite.graphics.endFill( );
et
.n
dn
cs
/b !
g.
7.14. 用位图填充图形
:/ 译
lo
tp 翻
ht 青
问题
常
我要用位图填充图形
解决办法
使用Graphics.beginBitmapFill( )方法
讨论
Graphics.beginBitmapFill( )方法允许用位图填充图形,它接受以下参数:
bitmap
填充用的BitmapData对象
matrix
默认下不需要应用转换,也可指定flash.geom.Matrix对象进行位图的缩放,旋转,倾斜,透明等
变换。
repeat
168. 布尔值,指定是否平铺位图,默认为true。
smooth
布尔值,指示对位图进行光滑处理,默认为false。
下面的例子通过URL载入位图,拷贝到BitmapData对象上,使用BitmapData对象填充圆:
package {
import flash.display.Sprite;
import flash.geom.Matrix;
import flash.display.Loader;
import flash.net.URLRequest;
import flash.display.BitmapData;
import flash.events.Event;
public class Drawing extends Sprite {
23
private var _loader:Loader;
01
ye
public function Drawing( ) {
in
ix
_loader = new Loader( );
/l
et
_loader.load(new URLRequest("http://www.rightactionscript.com/samplefiles/image2.jpg"));
.n
_loader.contentLoaderInfo.addEventListener(Event.COMPELTE, onImageLoad);
dn
cs
}
/b !
g.
:/ 译
lo
private function onImageLoad(event:Event):void {
tp 翻
ht 青
var bitmap:BitmapData = new BitmapData(_loader.width, _loader.height);
常
bitmap.draw(_loader, new Matrix( ));
var matrix:Matrix = new Matrix( );
matrix.scale(.1, .1);
var sampleSprite:Sprite = new Sprite( );
sampleSprite.graphics.lineStyle( );
sampleSprite.graphics.beginBitmapFill(bitmap, matrix);
sampleSprite.graphics.drawCircle(100, 100, 100);
sampleSprite.graphics.endFill( );
addChild(sampleSprite);
}
}}
169. 7.15. 编写遮罩
问题
我要创建遮罩
解决办法
使用DisplayObject.mask.
讨论
遮罩可用来创建唯一的图形或视觉效果。例如,可以创建擦除和过渡效果。还可以创建有趣的
动画效果,甚至是图形扭曲效果。
任何 可 视 化 对 象 都 可 以 作 为 任 一 对 象 的 遮 罩 , 下 面 的 代 码 把 sampleSprite 的遮 罩 设 置 为
maskSprite:
sampleSprite.mask = maskSprite;
23
下面的例子画了两个图形,一个作为遮罩。注意,两个对象都要通过addChild( )方法添加到显示
01
列表。虽然遮罩即使不添加到显示列表也能正常工作,不过还是建议你添加到显示列表:
ye
var maskSprite:Sprite = new Sprite( ); in
ix
/l
var pen:Pen = new Pen(maskSprite.graphics);
et
.n
pen.beginFill(0xFFFFFF);
dn
pen.drawArc(100, 100, 50, 80, 20, true);
cs
/b !
g.
pen.endFill( );
:/ 译
lo
tp 翻
var maskedSprite:Sprite = new Sprite( );
ht 青
常
maskedSprite.graphics.lineStyle( );
maskedSprite.graphics.beginFill(0xFF0000);
maskedSprite.graphics.drawRect(0, 0, 200, 200);
maskedSprite.graphics.endFill( );
maskedSprite.mask = maskSprite;
addChild(maskedSprite);
addChild(maskSprite);
下面的例子演示遮罩跟随鼠标移动。通过载入的图片指定遮罩,该遮罩跟着鼠标移动:
var loader:Loader = new Loader( );
loader.load(new URLRequest("http://www.rightactionscript.com/samplefiles/image2.jpg"));
addChild(loader);
var maskSprite:Sprite = new Sprite( );
170. maskSprite.graphics.lineStyle( );
maskSprite.graphics.beginFill(0xFFFFFF);
maskSprite.graphics.drawCircle(0, 0, 50);
maskSprite.graphics.endFill( );
loader.mask = maskSprite;
addChild(maskSprite);
maskSprite.startDrag(true);
8.0. 简介
和Flash 8 中的BitmapData类一样,这是非常重要的一个类,起初,Flash只是基于矢量的一个工
23
01
具,矢量图形是由数学方法描述图形元素,比如一条直线是从 x0, y0 扩展到x1, y1。 而一个位
ye
图,它把图形描述为一个矩形区域值集合,每个点都对应一个颜色值。
in
ix
矢量图有两大优势:缩放和文件大小。当你缩放矢量图时,图像总能保持清晰,而位图当放大
/l
时就会出现锯齿状变得不清晰。
et
.n
正因为矢量图是一个线条,曲线和图形的坐标组成的列表,所以它的文件和位图相比小很多。
dn
而位图要记录每个点的颜色值,信息量非常大,也就导致了文件的庞大。
cs
/b !
g.
矢量图的这种优势使它作为Flash媒体格式的最好形式,但是位图也有它自己的优点,比如位图
:/ 译
lo
tp 翻
能很好的表现照片,如果用矢量图去描述照片的所有颜色和图形,那结果就是比位图的文件还
ht 青
要大了。
常
位图另一个好处就是很容易进行处理,矢量图的每个曲线都是经过计算出来的,如果图像复杂
就会花很长时间去处理,而位图就容易处理多了,无论多么复杂的动画,位图总能表现的很好。
Flash 8 对位图的支持还是很有限的,虽然能够载入位图显示,但不能作过多的处理。BitmapData
类提供了很好的方法来操作位图。
171. 8.1. 创建BitmapData
BitmapData
BitmapData对象
问题
我要在程序里创建位图
解决办法
使用BitmapData类构造一个新的BitmapData对象
讨论
BitmapData类表示一个由象素组成的位图,包含了很多内建的方法控制和处理图像。第一步先
创建该类实例:
var bitmap:BitmapData = new BitmapData(width, height,transparent, fillColor);
该类在flash.display包中,width和height 参数指定位图的宽度和高度,下一个参数为布尔型,指
定是否创建alpha通道,fillColor 参数决定背景颜色。
23
width和height是必须的,transparent和fillColor默认为true和0xFFFFFFFF。
01
fillColor 接受32-bit的颜色值,这意味着它支持alpha通道。当然,相应的BitmapData的transparent
ye
属性应为true。否则,所以颜色都是不透明的。 in
ix
/l
下面的例子创建了BitmapData 对象,初始化为透明的带alpha通道,背景色为0:
et
.n
var bitmap:BitmapData = new BitmapData(100, 100, true, 0x00FFFFFF);
dn
创建出 BitmapData 实例后就存在于内存中了,虽然现在就可以创建内容,不过在没加入到显示
cs
/b !
g.
列表中之前它是不显示的。
:/ 译
lo
tp 翻
ht 青
常
172. 8.2. 添加位图到可视化对象列表
问题
我要创建BitmapData 并显示它
解决办法
使用BitmapData 创建位图,并加入到可视化对象列表。
讨论
在ActionScript 3.0里要让对象可视,则必须加入到可视化对象列表中才行,然而 addChild( ) 方
法添加的对象必须是flash.display.DisplayObject的子类才行,而BitmapData 类继承自Object,所
以不能直接加到列表中。
要加到可视化对象列表中,可使用flash.display.Bitmap类,它是DisplayObject.类的子类,实际上
是BitmapData的一个包装类,允许BitmapData可被显示。
当用 Bitmap构造函数创建实例时把 BitmapData引用作为参数传递给它,然后调用 addChild( )把
23
01
Bitmap 加入到显示列表,下面的例子创建了红色背景的BitmapData :
ye
in
var bitmap:BitmapData = new BitmapData(100, 100, true, 0xffff0000);
ix
var image:Bitmap = new Bitmap(bitmap);
/l
et
addChild(image);
.n
dn
cs
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
173. 8.3. 绘制可视化对象到位图上
问题
我想把Sprite或其他显示对象上的内容绘制到位图上
解决办法
用BitmapData类的draw( )方法绘制内容
讨论
创建好的BitmapData,只是简单的黑色背景。你可能会把在sprite或其他可视化对象上已画好的
图形内容画到位图上,用draw( )方法就能做到,你只要把相关对象作为draw( )的参数就可以了,
也可以把flash.geom.Matrix类实例作为参数,Matrix类允许你对图形进行缩放,旋转,变换,或
倾斜等操作。该参数是可选的,不过你又要使用参数,但又不想做什么,可指定为null。你还可
以传递 ColorTransform 对象作为参数,它能在绘图前修改颜色,下面的例子画了一个sprite到
BitmapData上,名为_sprite,BitmapData 名为bitmap:
23
bitmap.draw(_sprite);
01
ye
BitmapData 类有一些基本绘图方法,比如设置象素颜色,创建填充的矩形,或噪波函数,但是
没有基本的函数如画直线,曲线等等,为了解决这问题,可以先在movie clip或sprite画好,在把
in
ix
/l
对象画到位图上去。下面的例子创建了BitmapData和Sprite,先在sprite 上画出椭圆,再把sprite
et
画到BitmapData上:
.n
dn
var bitmap:BitmapData = new BitmapData(100, 100,true, 0x00ffffff);
cs
/b !
g.
var sprite:Sprite = new Sprite( );
:/ 译
lo
tp 翻
sprite.graphics.beginFill(0xff0000, 100);
ht 青
常
sprite.graphics.drawEllipse(0, 25, 100, 50);
sprite.graphics.endFill( );
bitmap.draw(sprite);s
174. 8.4. 载入外部图片到位图上
问题
我要载入外部图片作为BitmapData处理
解决办法
使用 flash.display.Loader 类载入图片,当图片载入完成时,通过loader的content 属性property,
它就是个Bitmap。访问Bitmap的bitmapData 属性就在访问载入的图片
讨论
通过Loader类载入外部位图。通过URLRequest 对象和位图的URL,监听loader的complete事件确
定是否载入完成:
package {
import flash.display.Sprite;
23
import flash.display.Loader;
01
ye
import flash.events.Event;
import flash.net.URLRequest;
in
ix
/l
public class BitmapLoader extends Sprite {
et
.n
private var _loader:Loader = new Loader( );
dn
cs
public function BitmapLoader( ){
/b !
g.
:/ 译
lo
_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
tp 翻
ht 青
_loader.load(new URLRequest("image.jpg"));
常
}
}
}
public function onComplete(event:Event):void {
var image:Bitmap = Bitmap(_loader.content);
var bitmap:BitmapData = image.bitmapData;
addChild(image);
}
首先得到loader的content 属性,它是个可视化对象表示内容被载入。 如果你载入的是外部的.swf,
它就是MovieClip类型。 在这里载入的是位图, 所以content 是一个Bitmap,类型转化为Bitmap 编
译器不会抱错。
接着就可以访问包含位图内容的BitmapData了,你可以修改或载入新的图片。下面的例子在图
175. 片上画了个白色的矩形:
public function onComplete(event:Event):void {
var loadedImage:Bitmap = Bitmap(_loader.content);
// Create a new Bitmap data and draw the
// loaded image to it.
var bitmap:BitmapData = new BitmapData(loadedImage.width,
loadedImage.height,
false, 0xffffffff);
bitmap.draw(loadedImage, new Matrix( ))
// Create a new Bitmap using the BitmapData
// and display it.
var image:Bitmap = new Bitmap(bitmap);
23
addChild(image);
01
ye
// Manipulate the pixels as you wish
in
ix
bitmap.fillRect(new Rectangle(0, 0, 50, 50), 0xffffffff);
/l
et
}
.n
dn
cs
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
177. import flash.events.Event;
public class ColorChooser extends Sprite {
private var _bitmap:BitmapData;
private var _textfield:TextField;
public function ColorChooser( ) {
_bitmap = new BitmapData(100, 100, false, 0xffffffff);
var image:Bitmap = new Bitmap(_bitmap);
addChild(image);
_bitmap.noise(1000, 0, 255, 1|2|4, false);
_textfield = new TextField( );
addChild(_textfield);
_textfield.y = 100;
23
addEventListener(Event.ENTER_FRAME, onEnterFrame);
01
ye
}
in
ix
public function onEnterFrame(event:Event):void {
/l
et
var colorVal:Number = _bitmap.getPixel(mouseX, mouseY)
.n
_textfield.text = "#" + colorVal.toString(16).toUpperCase( );
dn
cs
}
/b !
g.
:/ 译
lo
}
tp 翻
ht 青
}
常
178. 8.6. 创建矩形填充
问题
我要用指定的颜色填充一个位图的矩形区域
解决办法
使用BitmapData类的fillRect( )方法
讨论
BitmapData 类没有提供绘图方法,只有些填充方法,使用起来也是很简单的,只要传递一个矩
形和颜色就可以了:
_bitmap.fillRect(rectangle, color);
该矩形必须是flash.geom.Rectangle 类的实例。通过它的构造函数创建一个实例:
var rect:Rectangle = new Rectangle(0, 0, 50, 100);
23
下面的代码创建了一个白色背景的位图,然后在中间画了个红色的矩形:
01
ye
public function RectExample( ) {
_bitmap = new BitmapData(100, 100, false, 0xffffffff);
in
ix
/l
var image:Bitmap = new Bitmap(_bitmap);
et
.n
addChild(image);
dn
cs
_bitmap.fillRect(new Rectangle(25, 25, 50, 50), 0xffff0000);
/b !
g.
:/ 译
lo
}
tp 翻
注意 fillRect( ) 可以同时处理透明和不透明位图,如果对应不透明位图,只要指定颜色为 24 位
ht 青
常
即可。
179. 8.7. 创建一个充溢填充
问题
我要填充一个大的不规则的区域
解决办法
使用BitmapData类的floodFill( )方法
讨论
floodFill( )方法和setPixel( )方法语法一样,接受一个x,y坐标和颜色。
看下面的代码演示,首先创建一个位图和一些随机的方框,然后鼠标点击某个方框,就会用红
色填充它:
package {
import flash.display.Sprite;
23
import flash.display.Bitmap;
01
ye
import flash.display.BitmapData;
in
ix
import flash.events.MouseEvent;
/l
et
import flash.geom.Rectangle;
.n
dn
public class FloodFillDemo extends Sprite {
cs
private var _bitmap:BitmapData;
/b !
g.
:/ 译
lo
public function FloodFillDemo ( ) {
tp 翻
ht 青
var sprite:Sprite = new Sprite( );
常
addChild(sprite);
_bitmap = new BitmapData(stage.stageWidth,
stage.stageHeight,
false, 0xffffffff);
for(var i:int = 0; i < 20; i++) {
_bitmap.fillRect(new Rectangle(
Math.random( ) * stage.stageWidth,
Math.random( ) * stage.stageHeight,
50, 50), Math.random( ) * 0xffffffff);
}
var image:Bitmap = new Bitmap(_bitmap);
180. sprite.addChild(image);
sprite.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
}
public function onMouseDown(event:MouseEvent):void {
_bitmap.floodFill(mouseX, mouseY, 0xffff0000);
}
}
}
8.8. 拷贝象素
23
01
问题
ye
我要拷贝BitmapData中的象素 in
ix
解决办法
/l
et
使用BitmapData的copyPixels( )方法
.n
dn
讨论
cs
/b !
g.
copyPixels( )方法的实现也很简单,只要得到象素值然后画到其他地方,非常类似于draw( )方法 。
:/ 译
lo
但是copyPixels( )可控制拷贝象素的数量和目标。只要指定一个矩形区域和目标点即可:
tp 翻
ht 青
bitmap.copyPixels(sourceBmp, srcRect, destPoint);
常
srcRect是flash.geom.Rectangle类实例,destPoint是flash.geom.Point类实例,它指定拷贝到目标位
图的具体x,y坐标位置。
下面的例子演示如何从载入的位图中拷贝多个矩形区域到另一个BitmapData上:
package {
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.net.URLRequest;
import flash.events.Event;
import flash.geom.Point;
181. import flash.geom.Rectangle;
public class AS3CB extends Sprite {
private var _bitmap:BitmapData;
private var _loader:Loader;
public function AS3CB( ) {
_loader = new Loader( );
_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoad);
_loader.load(new URLRequest("myphoto.jpg"));
_bitmap = new BitmapData(stage.stageWidth,
stage.stageHeight,
false, 0xffffffff);
var image:Bitmap = new Bitmap(_bitmap);
23
addChild(image);
01
ye
}
in
ix
public function onLoad(event:Event):void {
/l
et
var loaderBmp:Bitmap = Bitmap(_loader.content);
.n
var w:Number = loaderBmp.width / 10;
dn
cs
for(var i:int = 0; i < 10; i++) {
/b !
g.
:/ 译
lo
_bitmap.copyPixels(loaderBmp.bitmapData,
tp 翻
ht 青
new Rectangle(i * w, 0,
常
w, loaderBmp.height),
new Point(i * (w + 2), i));
}
}
}
}
182. 8.9.拷贝通道
8.9.
问题
我要从BitmapData中拷贝出红色,绿色,蓝色或alpha通道出来
解决办法
使用BitmapData的copyChannel( )方法
讨论
copyChannel( )方法是另一个在两个位图之间交换数据的方法。它的前三个参数和 copyPixels( )
方法一样,另外还有源通道和目标通道:
bitmap.copyPixels(sourceBmp, srcRect, destPoint,
srcChannel, destChannel);
这两个通道参数可以1,2,4或8中的整数,它们代表红色,绿色,蓝色和alpha通道,一次对应
23
BitmapDataChannel类中的RED, GREEN, BLUE,和ALPHA常量。
01
你只要告诉方法从原始图像的什么通道拷贝到目标位图的哪个通道上,下面的代码拷贝载入位
ye
图的红色,绿色和蓝色通道到另一个位图上: in
ix
/l
var loaderBmp:Bitmap = Bitmap(loader.content);
et
.n
bitmap.copyChannel(loaderBmp.bitmapData,
dn
loaderBmp.bitmapData.rect,
cs
/b !
g.
new Point( ),
:/ 译
lo
tp 翻
BitmapDataChannel.RED,
ht 青
常
BitmapDataChannel.RED);
bitmap.copyChannel(loaderBmp.bitmapData,
loaderBmp.bitmapData.rect,
new Point(5, 5),
BitmapDataChannel.GREEN,
BitmapDataChannel.GREEN);
bitmap.copyChannel(loaderBmp.bitmapData,
loaderBmp.bitmapData.rect,
new Point(10, 10),
BitmapDataChannel.BLUE,
BitmapDataChannel.BLUE);
184. bitmap.noise(1000, 0, 255, BitmapDataChannel.RED |
BitmapDataChannel.GREEN |
BitmapDataChannel.BLUE,
false);
最后,我们试验下噪波和其他滤镜一起使用的效果如何。下面的代码创建了噪波,然后应用一
个水平的模糊滤镜:
bitmap = new BitmapData(stage.stageWidth, stage.stageHeight,
false, 0xff000000);
bitmap.noise(1000, 128, 255, BitmapDataChannel.RED, true);
bitmap.applyFilter(bitmap,
bitmap.rect,
new Point( ),
23
new BlurFilter(30, 1, 3));
01
ye
var image:Bitmap = new Bitmap(bitmap);
addChild(image);
in
ix
/l
et
.n
dn
cs
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
185. 8.11. 创建布林噪波
问题
我要创建随机的类似天然的效果如云,烟或水
解决办法
使用BitmapData类的perlinNoise( )方法
讨论
和noise( )方法一样,perlinNoise( )方法也创建随机图案,但是布林噪波的算法能产生类似自然图
案,该算法由肯 布林发明,能产生如爆炸,烟雾,水等自然效果,因为它是基于算法的,其运
算速度比创建同等位图快且占用内存少等优点,方法原型如下:
bitmap.perlinNoise(baseX, baseY, octaves, seed, stitch, fractal,
channels, grayscale, offsets);
23
前6个参数是必须的,后3个可选,现在我们创建一个简单的例子,下面的代码创建一个位图,
01
应用布林噪波并显示:
ye
in
bitmap = new BitmapData(stage.stageWidth, stage.stageHeight, false, 0xff000000);
ix
/l
bitmap.perlinNoise(100, 100, 1, 1000, false, false, 1, true, null);
et
.n
var image:Bitmap = new Bitmap(bitmap);
dn
addChild(image);
cs
/b !
g.
效果如下:
:/ 译
lo
tp 翻
ht 青
常
试着修改下参数值看看效果如何。
baseX和baseY 决定图案的大小,这里设置成100,如果改成200和50,则会在水平上进行拉伸,
效果如下:
186. octaves 参数是个整数,决定噪波的迭代次数,数值越大,产生越细的噪波,花费的时间也长一
些。
seed参数和noise( )方法中的一样意思,如果用同样的随机种子,产生的是同一个图案。
stitch参数为true时,图案四周相互协调,能使位图很小能平铺,看下面的代码:
bitmap = new BitmapData(100, 100, false, 0xff000000);
23
bitmap.perlinNoise(100, 100, 2, 1000, true, false, 1, true);
01
ye
graphics.beginBitmapFill(bitmap);
in
ix
graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
/l
graphics.endFill( );
et
.n
ractal 参数为true时,图案被光滑且模糊化:
dn
cs
bitmap = new BitmapData(stage.stageWidth, stage.stageHeight,
/b !
g.
:/ 译
lo
false, 0xff000000);
tp 翻
ht 青
bitmap.perlinNoise(200, 100, 5, 1000, false, false, 1, true, null);
常
var image:Bitmap = new Bitmap(bitmap);
addChild(image);
设置fractal为TRue:
bitmap.perlinNoise(200, 100, 5, 1000, false, true, 1, true, null);
这样的效果看起来像云
187. 23
01
ye
in
ix
/l
et
.n
dn
cs
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
后面2个参数为通道和灰度,和 noise( )方法类似。其值是 BitmapDataChannel类的RED, GREEN,
BLUE, 和ALPHA常量
bitmap.perlinNoise(200, 100, 5, 1000, false, true,
BitmapDataChannel.RED, false, null);
下面的代码产生彩色的图案:
bitmap.perlinNoise(200, 100, 5, 1000, false, true,
BitmapDataChannel.RED |
BitmapDataChannel.GREEN |
BitmapDataChannel.BLUE,
false, null);
188. 布林噪波还可建立在alpha通道上,这样可以创建透明的云彩或雾效果。
最后的参数表示偏移量,是个 Point 对象数组,每个点指定单个分形的x,y坐标偏移量,如果
布林噪波有多个分形(第三个参数代表分形) ,那么数组的长度就等于分形的数量,下面的例子
创建了两个分形布林噪波图案在x轴上拉伸:
package {
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.events.Event;
import flash.geom.Point;
public class Clouds extends Sprite {
private var _bitmap:BitmapData;
private var _xoffset:int = 0;
23
01
public function Clouds( ) {
ye
in
_bitmap = new BitmapData(stage.stageWidth, stage.stageHeight,
ix
/l
true, 0xffffffff);
et
.n
var image:Bitmap = new Bitmap(_bitmap);
dn
addChild(image);
cs
/b !
g.
addEventListener(Event.ENTER_FRAME, onEnterFrame);
:/ 译
lo
tp 翻
}
ht 青
常
public function onEnterFrame(event:Event):void {
_xoffset++;
var point:Point = new Point(_xoffset, 0);
// use the same point in both elements
// of the offsets array
_bitmap.perlinNoise(200, 100, 2, 1000, false, true,
1, true, [point, point]);
}
}
}
207. 设置为CENTER时根据文本框的中心,保持顶部位置不变,调整左右边框:
field.autoSize = TextFieldAutoSize.CENTER;
设置为RIGHT 时保持右上角位置不变,根据内容变化自动调整左下角位置:
field.autoSize = TextFieldAutoSize.RIGHT;
wordWrap属性设为true时当内容超出范围时自动移到下一行,也就说保持水平宽度不变,如果
为false则自动调整水平宽度不换行:
var field:TextField = new TextField( );
field.autoSize = TextFieldAutoSize.LEFT;
field.text = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi tortor purus, aliquet a,
ornare ac, suscipit a, est. Nullam hendrerit molestie erat. Nunc nulla tortor, ullamcorper et, elementum
vel, fringilla sed, dui. Praesent fermentum interdum orci.";
addChild(field);
下面再添加一行字符串:
23
01
var field:TextField = new TextField( );
ye
field.autoSize = TextFieldAutoSize.LEFT; in
ix
field.text = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi tortor purus, aliquet a,
/l
et
ornare ac, suscipit a, est.";
.n
dn
field.text += "n";
cs
field.text += "Nullam hendrerit molestie erat. Nunc nulla tortor, ullamcorper et, elementum vel,
/b !
g.
:/ 译
lo
fringilla sed, dui. Praesent fermentum interdum orci.";
tp 翻
ht 青
addChild(field);
常
wordWrap 属性为true进行自动换行:
var field:TextField = new TextField( );
field.autoSize = TextFieldAutoSize.LEFT;
field.wordWrap = true;
field.text = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi tortor purus, aliquet a,
ornare ac, suscipit a, est. Nullam hendrerit molestie erat. Nunc nulla tortor, ullamcorper et, elementum
vel, fringilla sed, dui. Praesent fermentum interdum orci.";
addChild(field);
210. 最快最简单的方式就是使用HTML标签。例如,下面的代码显示粗体和下划线文字:
field.html = true;
field.htmlText = "<b>Bold text</b> <u>Underlined text</u>";
使用TextFormat对象进行格式化稍微复杂些,首先要先创建TextFormat对象:
var formatter:TextFormat = new TextFormat( );
接着,对TextFormat对象的一些属性进行赋值:
formatter.bold = true; // 设置粗体
formatter.color = 0xFFFF00; // 设置文本颜色为黄色
formatter.blockIndent = 5; //调整间距为5
通过setTextFormat( )方法应用:
field.setTextFormat(formatter);
通过setTextFormat( )方法格式化只对当前的文本起作用,如果之后又加入文本则不会进行格式
23
化:
01
ye
field.text = "this is sample text";
field.setTextFormat(formatter); // 应用格式化
in
ix
/l
field.text = "this is new text"; // 没有应用格式化
et
.n
field.setTextFormat(formatter); // 再次应用格式化
dn
cs
field.text += "appended text"; // 格式化被取消
/b !
g.
:/ 译
lo
如果改变了TextFormat对象则需重新调用setTextFormat( )方法。
tp 翻
使用flash.text.StyleSheet类可支持CSS。StyleSheet构造函数不需要任何参数:
ht 青
常
var css:StyleSheet = new StyleSheet( );
下面的表格列出了 CSS 属性和相应的 ActionScript 属性:
CSS 属性 ActionScript 属性 描述
color color 十六进制值 #RRGGBB
display display 显示文本方式:inline,block,none
font-family fontFamily 字体类型
font-size fontSize 字体大小
font-style fontStyle normal 或 italic
font-weight fontWeight normal 或 bold
kerning kering true 或 false,只对嵌入字体有效
margin-left marginLeft 左边距
margin-right marginRight 右边距
text-align textAlign 对齐方式:left,center,right,justify
text-decoration textDecoration none,underline
211. text-indent textIndent 缩进
通过构造函数生成一个StyleSheet 对象,然后传递给setStyle( )方法。一个样式表对象实际上是一
个ActionScript样式数组,看下面的例子:
var sampleStyle:Object = new Object( );
sampleStyle.color = "#FFFFFF";
sampleStyle.textAlign = "center";
下面的代码等同于上面的代码:
var sampleStyle:Object = {color: "#FFFFFF", textAlign: "center"};
然后调用类的setStyle( )方法应用样式:
css.setStyle(".sample", sampleStyle);
虽然我们可以这样定义,但实际应用中很少这样,一般都是读取CSS文档,这样改变样式时只
要修改CSS文档也不必每次重新编译.swf文件。
23
01
可通过flash.net.URLLoader类载入CSS文档。一旦读入文档,只要生成一个StyleSheet对象然后传
ye
递给对象的parseCSS( )方法即可。下面的例子载入styles.css,一个CSS文档:
in
ix
p{
/l
et
font-family: _sans;
.n
dn
color: #FFFFFF;
cs
/b !
g.
}
:/ 译
lo
.emphasis {
tp 翻
ht 青
font-weight: bold;
常
font-style: italic;
}
package {
import flash.display.Sprite;
import flash.text.TextField;
import flash.events.Event;
import flash.text.TextFieldAutoSize;
import flash.text.StyleSheet;
import flash.net.URLLoader;
import flash.net.URLRequest;
public class CSSText extends Sprite {
212. public function CSSText( ) {
var loader:URLLoader = new URLLoader( );
loader.addEventListener(Event.COMPLETE, onLoadCSS);
var request:URLRequest = new URLRequest("styles.css");
loader.load(request);
}
private function onLoadCSS(event:Event):void {
var css:StyleSheet = new StyleSheet( );
css.parseCSS(URLLoader(event.target).data);
var field:TextField = new TextField( );
field.autoSize = TextFieldAutoSize.LEFT;
field.wordWrap = true;
23
field.width = 200;
01
ye
addChild(field);
in
ix
field.styleSheet = css;
/l
et
field.htmlText = "<p><span class='emphasis'>Lorem ipsum</span> dolor sit amet,
.n
consectetuer adipiscing elit. Morbi tortor purus, aliquet a, ornare ac, suscipit a, est. Nullam hendrerit
dn
molestie erat. Nunc nulla tortor, ullamcorper et, elementum vel, fringilla sed, dui. Praesent fermentum
cs
/b !
g.
interdum orci.</p>";
:/ 译
lo
tp 翻
}
ht 青
常
}
}
这里需要注意几点:
只有当文本框渲染为HTML时才可应用CSS。
HTML和CSS必须配合,比如CSS里定义了一个叫someCSSClass的类,那么HTML也必须有。
必须先应用CSS,再应用HTML。
如果用户想选择不同的CSS样式,我们可把HTML文本存在变量或数组中,这样每次新的CSS被
载入时就可以重新应用了:
package {
import flash.display.Sprite;
import flash.text.TextField;
import flash.events.Event;
import flash.events.MouseEvent;
213. import flash.text.TextFieldAutoSize;
import flash.text.StyleSheet;
import flash.net.URLLoader;
import flash.net.URLRequest;
public class CSSText extends Sprite {
private var _field:TextField;
private var _html:String;
public function CSSText( ) {
var css1:TextField = new TextField( );
css1.text = "stylesheet 1";
css1.selectable = false;
var css1Container:Sprite = new Sprite( );
23
css1Container.addEventListener(MouseEvent.CLICK, onCSS1);
01
ye
css1Container.addChild(css1);
in
ix
addChild(css1Container);
/l
et
var css2:TextField = new TextField( );
.n
css2.text = "stylesheet 2";
dn
cs
css2.selectable = false;
/b !
g.
:/ 译
lo
var css2Container:Sprite = new Sprite( );
tp 翻
ht 青
css2Container.addEventListener(MouseEvent.CLICK, onCSS2);
常
css2Container.addChild(css2);
addChild(css2Container);
css2Container.y = 25;
_field = new TextField( );
_field.autoSize = TextFieldAutoSize.LEFT;
_field.wordWrap = true;
_field.width = 200;
addChild(_field);
_html = "<p><span class='emphasis'>Lorem ipsum</span> dolor sit amet, consectetuer
adipiscing elit. Morbi tortor purus, aliquet a, ornare ac, suscipit a, est. Nullam hendrerit molestie erat.
Nunc nulla tortor, ullamcorper et, elementum vel, fringilla sed, dui. Praesent fermentum interdum
orci.</p>";
214. _field.y = 50;
}
private function loadCSS(url:String):void {
var loader:URLLoader = new URLLoader( );
loader.addEventListener(Event.COMPLETE, onLoadCSS);
var request:URLRequest = new URLRequest(url);
loader.load(request);
}
private function onCSS1(event:MouseEvent):void {
loadCSS("styles.css");
}
private function onCSS2(event:MouseEvent):void {
23
loadCSS("styles2.css");
01
ye
}
in
ix
private function onLoadCSS(event:Event):void {
/l
et
var css:StyleSheet = new StyleSheet( );
.n
css.parseCSS(URLLoader(event.target).data);
dn
cs
_field.styleSheet = css;
/b !
g.
:/ 译
lo
_field.htmlText = _html;
tp 翻
ht 青
}
常
}
}
9.14. 格式化用户输入的文本
问题
我要对用户输入的文本进行格式化
解决办法
应用TextFormat 对象到文本框的defaultTextFormat 属性上
讨论
215. 通过使用 defaultTextFormat 属性即可对用户输入的文本进行格式化,只要创建一个 TextFormat
对象赋值给defaultTextFormat 属性:
var formatter:TextFormat = new TextFormat( );
formatter.color = 0x0000FF; // 设置颜色为蓝色
field.defaultTextFormat = formatter;
9.15. 格式化一部分文本
问题
我想只格式化一部分文本而不是全部,或者应用多种格式化到文本的不同部分上
解决办法
创建TextFormat 对象的setTextFormat( ) 方法对部分文本进行格式化
23
01
讨论
ye
第9.13节已经讨论了格式化整个文本,实际上通过setTextFormat( )方法可以格式化部分文本,需 in
ix
要增加其他参数:
/l
et
index
.n
dn
对对应位置的字符格式化。
cs
/b !
g.
textFormat
:/ 译
lo
tp 翻
TextFormat对象引用。
ht 青
下面的例子对第一个字符格式化:
常
field.setTextFormat(0, formatter);
要想对一定范围的字符进行格式化,需要给出三个参数:
startIndex
格式化起始位置
endIndex
格式化结束位置
textFormat
TextFormat 对象引用
下面的例子对开始的前10个字符格式化:
field.setTextFormat(0, 10, formatter);
216. 9.16. 设置文本框字体
问题
我要更改文本框的字体
解决办法
使用HTML的<font>标签,或者设置TextFormat对象的font属性,或者通过CSS的font-family属性
讨论
修改字体有多种方法,如果使用HTML的话可通过<font> 标签更改:
field.htmlText = "<font face='Arial'>Formatted text</font>";
也可设置TextFormat对象的font属性:
formatter.font = "Arial";
或者在CSS中定义font-family 属性:
23
01
p{
ye
font-family: Arial; in
ix
}
/l
et
需要注意的是电脑中必须要有你所指定的字体,因为有些电脑上可能没有安装相应的字体,这
.n
是可指定多种字体:
dn
cs
formatter.font = "Arial, Verdana, Helvetica";
/b !
g.
:/ 译
lo
tp 翻
如果都没有指定字体,默认使用系统字体。另外我们还可使用字体组,字体组是系统默认字体
ht 青
常
的一个分类,有三种: _sans, _serif, 和_typewriter。 _sans 组包含如 Arial 或 Helvetica,_serif
组包含如 Times 或 Times New Roman,_typewriter 组包含如 Courier 或 Courier New。
217. 9.17. 嵌入字体
问题
我要嵌入自己的字体
解决办法
通过[Embed] 元数据嵌入字体,设置文本框的 embedFonts 属性为true,通过<font> 标签,
TextFormat 对象或CSS应用字体
讨论
当用户电脑上没有相应字体时可以通过[Embed]元数据把字体嵌入到swf中,[Embed] 元数据在
类外面申明,可以嵌入TrueType 字体或系统字体,语法如下:
[Embed(source="pathToTtfFile", fontName="FontName", mimeType="application/x-font-truetype")]
TrueType f字体路径可以是相对或绝对路径,比如:
23
[Embed(source="C:WindowsFontsExample.ttf",fontName="ExampleFont",
01
mimeType="application/x-font-truetype")]
ye
fontName 属性可被CSS或ActionScript引用。 in
ix
/l
嵌入系统字体语法类似,只是systemFont 属性代替了source 属性。systemFont 属性指定嵌入的
et
系统字体,下面的例子嵌入Times New Roman字体:
.n
dn
[Embed(systemFont="Times New Roman",fontName="Times New Roman",
cs
mimeType="application/x-font-truetype")]
/b !
g.
:/ 译
lo
tp 翻
ht 青
嵌入好字体后,下一步就是告诉文本框使用嵌入的字体,可设置embedFonts 属性为true:
常
field.embedFonts = true;
接下来通过<font> 标签,TextFormat 对象或CSS使用之。例如,如果fontName 值为Times New
Roman:
formatter.font = "Times New Roman";
使用<font>标签:
field.htmlText = "<font family='Times New Roman'>Example</font>";
使用CSS:
var css:StyleSheet = new StyleSheet( );
css.setStyle("p", {fontFamily: "Times New Roman"});
field.htmlText = "<p>Example</p>";
218. 9.18. 创建可以被旋转的文字
问题
我要使一些文字在旋转时仍能正确显示
解决办法
使用嵌入字体
讨论
一般情况下默认字体是最好的,但是有些特殊情况如文本框被旋转或者它的父容器被旋转,这
时默认字体渲染得文字就不能显示了,这时候必须使用嵌入字体。
9.19. 显示Unicode
Unicode
Unicode编码的文字
23
01
ye
问题 in
ix
我要显示Unicode编码的文字,可能包括非英文字符
/l
et
解决办法
.n
dn
载入外部源文本,使用Unicode转义序列将字符赋值给文本框的text 属性
cs
/b !
g.
讨论
:/ 译
lo
tp 翻
如果想在文本框中显示Unicode文本,可通过以下几种方式:
ht 青
载入外部Unicode源数据(如文本文件,XML文档,数据库)。
常
直接使用字符。
使用Unicode转义序列。
在支持Unicode的编辑器(如Flex Builder),可直接输入字符,因为都是经过Unicode编 码 过 了 ,
如果你知道字符的转义序列,也可把它赋值给文本框的text属性,在ActionScript里Unicode转义
序列以u开头后面跟着4个十六进制数字,而且要放在括号中,如下面的例子:
field.text = "Add a registered mark directly (®) or with a Unicode escape sequence (u00AE)";
219. 9.20. 设置文本框的焦点
问题
我想用ActionScript 设置文本框焦点
解决办法
使用Stage.focus 属性
讨论
使用Stage.focus属性可在程序里赋值焦点到一个文本框上,每个可视化对象都有一个stage 属性,
它是Stage类实例,下面的代码把焦点赋值给叫field的文本框上:
stage.focus = field;
当一个.swf 第一次被载入到浏览器时,它是没有焦点的,因此在必须先设定把焦点移到Flash播
放器上,下面的例子把焦点赋值给文本框:
23
package {
01
ye
import flash.display.Sprite;
import flash.text.TextField;
in
ix
/l
import flash.text.TextFieldType;
et
.n
import flash.events.MouseEvent;
dn
cs
public class TextExample extends Sprite {
/b !
g.
:/ 译
lo
public function TextExample( ) {
tp 翻
ht 青
var field:TextField = new TextField( );
常
field.border = true;
field.background = true;
field.type = TextFieldType.INPUT;
addChild(field);
var button:Sprite = new Sprite( );
button.graphics.lineStyle( );
button.graphics.beginFill(0xFFFFFF);
button.graphics.drawRect(0, 0, 100, 50);
button.graphics.endFill( );
button.addEventListener(MouseEvent.CLICK, onClick);
button.y = 100;
addChild(button);
220. }
private function onClick(event:MouseEvent):void {
stage.focus = TextField(getChildAt(0));
}
}
}
把Stage.focus 设为null即可移除焦点:
stage.focus = null;
9.21. 用ActionScript
ActionScript
ActionScript实现选择文本
23
01
ye
问题
in
ix
我要选中一部分文字
/l
et
解决办法
.n
使用TextField.setSelection( )方法。
dn
cs
讨论
/b !
g.
:/ 译
lo
TextField.setSelection( )方法是以程序的方式选中一部分文本,它接受两个参数:
tp 翻
ht 青
startIndex
常
开始位置
endIndex
结束位置
调用该方法之前,文本框必须先拥有焦点,可通过Stage.focus进行设置:
stage.focus = field; //设置焦点
field.text = "this is example text"; // 设置文本内容
field.setSelection(0, 4); //选中"this"
通过 selectionBeginIndex 和 selectionEndIndex 两个只读属性可获得选中文本的具体位置。
242. 10.12. 改变饱和度
问题
我想改变对象的饱和度
解决办法
使用饱和度矩阵创建ColorMatrixFilter对象
讨论
饱和度矩阵:
a b c 0 0
d e f 0 0
g h i 0 0
0 0 0 1 0
23
01
具体计算公式看下面,i是饱和度值:
ye
a = (1 value) * red + value in
ix
b = (1 value) * green
/l
et
c = (1 value) * blue
.n
dn
d = (1 value) * red
cs
/b !
g.
e = (1 value) * green + value
:/ 译
lo
tp 翻
f = (1 value) * blue
ht 青
常
g = (1 value) * red
h = (1 value) * green
i = (1 value) * blue + value
当饱和度值为0时,这个矩阵就是灰度矩阵。
我们可使用ascb.filters.ColorMatrixArrays.getSaturationArray( )方法构造一个饱和度矩阵数组,只
需要传递饱和度值即可。
sampleSprite.filters = [new ColorMatrixFilter(ColorMatrixArrays.getSaturationArray(2))];
250. _timer = new Timer(30);
_timer.addEventListener("timer", onTimer);
_timer.start( );
}
public function onTimer(event:TimerEvent):void {
var vx:Number = (_targetX - _sprite.x) * _easingSpeed;
var vy:Number = (_targetY - _sprite.y) * _easingSpeed;
_sprite.x += vx;
_sprite.y += vy;
}
}
}
23
这里有个小问题,当物体到底目标位置时,定时器却仍然在运行,我们修改下代码,检测距离
01
小于1时停止定时器:
ye
public function onTimer(event:TimerEvent):void {
in
ix
/l
var dx:Number = _targetX - _sprite.x;
et
.n
var dy:Number = _targetY - _sprite.y;
dn
cs
var dist:Number = Math.sqrt(dx * dx + dy * dy);
/b !
g.
:/ 译
lo
if(dist < 1)
tp 翻
ht 青
{
常
_sprite.x = _targetX;
_sprite.y = _targetY;
_timer.stop( );
}
else
{
var vx:Number = dx * _easingSpeed;
var vy:Number = dy * _easingSpeed;
_sprite.x += vx;
_sprite.y += vy;
}
}
251. 上面的例子代码首先计算出两点之间的距离,如果距离小于1,则把物体移到目标位置上,然后
停止定时器。
有时候你可能不希望减速到停止位置,比如物体跟随鼠标移动,这里我们只需要把 _targetX和
_targetY替换为mouseX和mouseY即可::
public function onTimer(event:TimerEvent):void {
var vx:Number = (mouseX - _sprite.x) * _easingSpeed;
var vy:Number = (mouseY - _sprite.y) * _easingSpeed;
_sprite.x += vx;
_sprite.y += vy;
}
23
11.4. 加速运动
01
ye
in
ix
问题
/l
et
我想让物体加速移动
.n
解决办法
dn
cs
应用加速方法
/b !
g.
:/ 译
lo
讨论
tp 翻
ht 青
许多人认为加速只是简单的提高速度而已,比如想让车开的快些就踩一下加速器。更科学的定
常
义为速率的变化称为加速。虽然大多数情况只是提高物体的速度,实际上还包括减速和改变方
向。
下面的例子中变量_ax和_ay代表加速,_vx和_vy代表速率:
package {
import flash.display.Sprite;
import flash.events.Event;
public class Accel extends Sprite {
private var _sprite:Sprite;
private var _ax:Number = .3;
private var _ay:Number = .2;
private var _vx:Number = 0;
private var _vy:Number = 0;
252. public function Accel( ) {
_sprite = new Sprite( );
_sprite.graphics.beginFill(0x0000ff, 100);
_sprite.graphics.drawCircle(0, 0, 25);
_sprite.graphics.endFill( );
_sprite.x = 50;
_sprite.y = 100;
addChild(_sprite);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
public function onEnterFrame(event:Event):void {
_vx += _ax;
23
_vy += _ay;
01
ye
_sprite.x += _vx;
in
ix
_sprite.y += _vy;
/l
et
}
.n
}
dn
cs
}
/b !
g.
:/ 译
lo
除了改变速度,还可以改变方向:
tp 翻
ht 青
var angle:Number = 45;
常
var accel:Number = .5;
转换每个轴上的加速:
var radians:Number = angle * Math.PI / 180;
_ax = Math.cos(radians) * accel;
_ay = Math.sin(radians) * accel;
把这两个值添加到速率中。
253. 11.5. 弹跳
问题
我想让物体弹跳起来
解决办法
使用Hooke's定律---弹簧算法
讨论
Hooke's定律描述了弹簧的运动规律,一般弹簧都有不同的弹力即弹簧所拥有的能量,或大或小 ,
我们用_k变量表示弹簧能量的大小,设为0.1或0.2较好。
ActionScript的弹簧模型还需要个目标点作为物体的弹跳点,另外还需要设置一些阻尼系数,在
真实世界里物体弹跳过程会慢慢失去能量,设置能量属性为0.95表示每次弹跳丢失5%的 能 量 ,
直到物体停止跳动,下面的例子掩饰了弹簧的原理:
package {
23
01
import flash.display.Sprite;
ye
import flash.events.Event; in
ix
/l
public class Spring extends Sprite {
et
.n
private var _sprite:Sprite;
dn
private var _vx:Number = 20;
cs
/b !
g.
private var _vy:Number = 0;
:/ 译
lo
tp 翻
private var _k:Number = .1;
ht 青
常
private var _damp:Number = .94;
private var _targetX:Number = 200;
private var _targetY:Number = 200;
public function Spring( ) {
_sprite = new Sprite( );
_sprite.graphics.beginFill(0x0000ff, 100);
_sprite.graphics.drawCircle(0, 0, 25);
_sprite.graphics.endFill( );
_sprite.x = 0;
_sprite.y = 0;
addChild(_sprite);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
254. }
public function onEnterFrame(event:Event):void {
var ax:Number = (_targetX - _sprite.x) * _k;
var ay:Number = (_targetY - _sprite.y) * _k;
_vx += ax;
_vy += ay;
_sprite.x += _vx;
_sprite.y += _vy;
_vx *= _damp;
_vy *= _damp;
}
}
23
}
01
ye
把目标点改成鼠标坐标试试:
in
ix
var ax:Number = (mouseX - _sprite.x) * _k;
/l
et
var ay:Number = (mouseY - _sprite.y) * _k;
.n
dn
cs
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
255. 11.6. 使用三角定理
问题
我想做一些高级动画,如旋转,循环运动或摆动
解决办法
使用内建的函数:Math.sin( ),Math.cos( ),和Math.atan2( ).
讨论
11.2节和11.4节 已经使用了正弦和余弦函数,除此它们还被用来产生更有用的效果,比如围绕
一个中心或沿着直线做运动,旋转等效果。Math.sin( )和Math.cos( )函数都是基于正三角形(有
一个角等于90度) 。当增大函数的参数数值时,函数的返回值从-1到0, 1, 0, 回到-1, 如下代码所
示:
for(var i:Number = 0; i < 10; i += 0.1) {
trace(Math.sin(i));
23
01
}
ye
上面的代码输出一串数字,从0开始,到0.999,再到-0.999,再试着增大这个数字,比如40,那 in
ix
就会输出-40到40。利用正弦我们就能做出摆动效果,如下例子:
/l
et
package {
.n
dn
import flash.display.Sprite;
cs
/b !
g.
import flash.events.Event;
:/ 译
lo
tp 翻
public class Oscillation extends Sprite {
ht 青
常
private var _sprite:Sprite;
private var _angle:Number = 0;
private var _radius:Number = 100;
public function AS3CB( ) {
_sprite = new Sprite( );
_sprite.graphics.beginFill(0x0000ff, 100);
_sprite.graphics.drawCircle(0, 0, 25);
_sprite.graphics.endFill( );
_sprite.x = 0;
_sprite.y = 100;
addChild(_sprite);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
256. }
public function onEnterFrame(event:Event):void {
_sprite.x = 200 + Math.sin(_angle) * _radius;
_angle += .05;
}
}
}
这里的_angle变量保持增大,然后作为Math.sin( )的参数,输出结果在乘以_radius变量,它被设
为100,这样结果就是物体向左右摆动100像素。
Math.cos( )也能达到同样的效果,两个叠加形成圆周运动:
public function onEnterFrame(event:Event):void {
_sprite.x = 200 + Math.sin(_angle) * _radius;
23
_sprite.y = 200 + Math.cos(_angle) * _radius;
01
ye
_angle += .05;
}
in
ix
/l
如果要形成椭圆形运动,这设置不同的弧度值,如设置_xRadius为100,_yRadius为50:
et
.n
public function onEnterFrame(event:Event):void {
dn
cs
_sprite.x = 200 + Math.sin(_angle) * _xRadius;
/b !
g.
:/ 译
lo
_sprite.y = 200 + Math.cos(_angle) * _yRadius;
tp 翻
ht 青
_angle += .05;
常
}
如果要实现看似随即的运动轨迹的话,可独立设置每个轴的相关变量:
private var _xAngle:Number = 0;
private var _yAngle:Number = 0;
private var _xSpeed:Number = .13;
private var _ySpeed:Number = .09;
private var _xRadius:Number = 100;
private var _yRadius:Number = 50;
Then apply those to the motion code:
public function onEnterFrame(event:Event):void {
_sprite.x = 200 + Math.sin(_xAngle) * _xRadius;
_sprite.y = 200 + Math.cos(_yAngle) * _yRadius;
257. _xAngle += _xSpeed;
_yAngle += _ySpeed;
}
另一个有用的函数是Math.atan2( )。主要用它来计算出两点的夹角,它接受两个参数,两点在y
轴上的距离和在x轴上的距离,然会返回角度值。
下面的例子创建了“跟随眼睛”效果,桌面的小工具,它会一直看着鼠标:
package {
import flash.display.Sprite;
import flash.events.Event;
public class FollowingEye extends Sprite {
private var _sprite:Sprite;
public function AS3CB( ) {
23
_sprite = new Sprite( );
01
ye
_sprite.graphics.beginFill(0xffffff, 100);
_sprite.graphics.drawCircle(0, 0, 25);
in
ix
/l
_sprite.graphics.endFill( );
et
.n
_sprite.graphics.beginFill(0x000000, 100);
dn
cs
_sprite.graphics.drawCircle(20, 0, 5);
/b !
g.
:/ 译
lo
_sprite.graphics.endFill( );
tp 翻
ht 青
_sprite.x = 100;
常
_sprite.y = 100;
addChild(_sprite);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
public function onEnterFrame(event:Event):void {
var dx:Number = mouseX - _sprite.x;
var dy:Number = mouseY - _sprite.y;
var radians:Number = Math.atan2(dy, dx);
_sprite.rotation = radians * 180 / Math.PI;
}
}
}
258. 11.7. 运用动画技术
问题
我想把这章里的动画技术应用到对象的运动上
解决办法
运用这些技术,把结果赋值给对象的x和y属性
讨论
虽然改变对象的位置有各种各样的方法,大多数这章讨论的方法都可以被应用到一个电影剪辑
或sprite的任何属性上。
首先尝试应用速率到对象的rotation属性上,变量名为_vr:
package {
import flash.display.Sprite;
23
import flash.events.Event;
01
ye
public class AnimatingRotation extends Sprite {
in
ix
private var _sprite:Sprite;
/l
et
private var _vr:Number = 4;
.n
dn
public function AS3CB( ) {
cs
_sprite = new Sprite( );
/b !
g.
:/ 译
lo
_sprite.graphics.beginFill(0xffffff, 100);
tp 翻
ht 青
_sprite.graphics.drawRect(-50, -20, 100, 40);
常
_sprite.graphics.endFill( );
_sprite.x = 100;
_sprite.y = 100;
addChild(_sprite);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
public function onEnterFrame(event:Event):void {
_sprite.rotation += _vr;
}
}
}
259. 我们看到一个矩形在做旋转运动,每帧sprite的rotation增加4。
下面的例子应用弹簧原理缩放sprite。click处理函数产生随即的缩放值,enterFrame函数做弹簧
运动,点击sprite会产生新的大小:
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
public class AnimatingProperties extends Sprite {
private var _sprite:Sprite;
private var _k:Number = 0.1;
private var _damp:Number = 0.9;
private var _scaleVel:Number = 0;
23
private var _targetScale:Number = 1;
01
ye
public function AS3CB( ) {
_sprite = new Sprite( );
in
ix
/l
_sprite.graphics.beginFill(0xffffff, 100);
et
.n
_sprite.graphics.drawRect(-50, -50, 100, 100);
dn
cs
_sprite.graphics.endFill( );
/b !
g.
:/ 译
lo
_sprite.x = 100;
tp 翻
ht 青
_sprite.y = 100;
常
addChild(_sprite);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
_sprite.addEventListener(MouseEvent.CLICK, onClick)
}
public function onEnterFrame(event:Event):void {
_scaleVel += (_targetScale - _sprite.scaleX) * _k
_sprite.scaleX += _scaleVel;
_sprite.scaleY = _sprite.scaleX;
_scaleVel *= _damp;
}
public function onClick(event:MouseEvent):void {
_targetScale = Math.random( ) * 2 - .5;
260. }
}
}
下面的例子设置了两组颜色值: _red1, _green1, _blue1, 和 _red2, _green2, _blue2。每个值都在0.0
到1.0之间。在enterFrame处理函数中用这些值设置color transform 对象并应用到sprite上:
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.ColorTransform;
public class AnimatingColor extends Sprite {
private var _sprite:Sprite;
23
private var _red1:Number = 1;
01
ye
private var _green1:Number = 0;
private var _blue1:Number = 0;
in
ix
/l
private var _red2:Number = 0;
et
.n
private var _green2:Number = .5;
dn
cs
private var _blue2:Number = 1;
/b !
g.
:/ 译
lo
private var _easingSpeed:Number = 0.05;
tp 翻
ht 青
public function AS3CB( ) {
常
_sprite = new Sprite( );
_sprite.graphics.beginFill(0xffffff, 100);
_sprite.graphics.drawRect(-50, -50, 100, 100);
_sprite.graphics.endFill( );
_sprite.x = 100;
_sprite.y = 100;
addChild(_sprite);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
addEventListener(MouseEvent.CLICK, onClick);
}
public function onEnterFrame(event:Event):void {
_red1 += (_red2 - _red1) * _easingSpeed;
261. _green1 += (_green2 - _green1) * _easingSpeed;
_blue1 += (_blue2 - _blue1) * _easingSpeed;
_sprite.transform.colorTransform =
new ColorTransform(_red1, _green1, _blue1);
}
public function onClick(event:MouseEvent):void {
_red2 = Math.random( );
_green2 = Math.random( );
_blue2 = Math.random( );
}
}
}
23
01
ye
in
ix
/l
et
12.0. 简介
.n
dn
cs
/b !
g.
在ActionScript里字符串是最基本的字符存储类型。一个字符串由双引号或单引号包围的零个或
:/ 译
lo
多个字符组成。和其他语言不同的是在ActionScript里单引号和双引号是没有区别的,例如:
tp 翻
ht 青
var exampleA:String = "this is a string";
常
var exampleB:String = 'this is also a string';
var exampleC:String = "strings can contain characters such as (*+5~";
var exampleD:String = ""; // 空字符串
var exampleE:String = "x"; //单个字符
var exampleF:String; // 默认为null
字符串必须在引号之内,而且单引号和双引号不能混用,下面的两种写法都是错误的:
var exampleA:String = "an incorrect string'; // 必须以双引号结尾
var exampleB:String = 'another incorrect string"; // 必须以单引号结尾
ActionScript 提供了很多方法进行字符串的处理计算,在 ActionScript 3.0 增加了支持字符串的
正则表达式(模式匹配),具体内容请看第十三章。
262. 12.1. 字符串连接
问题
我想把零散的多个字符串连接成一个
解决办法
使用连接操作符+,或者简写成+=,或者使用String.concat( )方法
讨论
使用+操作符可把多个字符串连接成一个字符串:
// 连接成的字符串为"Thisworks" (中间没有空格)
var example:String = "This" + "works";
可一次连接多个字符串:
// 结果为"This works" (中间有空格)
23
var example:String = "This" + " " + "works";
01
ye
还可以连接其他类型的数据(自动转换为字符串),例如:
in
ix
var attendance:int = 24;
/l
et
// 结果为"There are 24 people"
.n
dn
var output:String = "There are " + attendance + " people";
cs
+操作符会在连接之前把其他类型的数据转换为字符串后再进行连接,上面的例子把整型的 24
/b !
g.
:/ 译
lo
转换为字符串进行了连接,但是如果所有的待连接的数据都是数字,那编译器就会报错:
tp 翻
ht 青
var first:int = 24;
常
var second:int = 42;
// 结果编译器报错"Implicit coercion of a value
// type 'Number' to an unrelated type 'String'"
var result:String = first + second;
这时有个技巧就是在前面加个空字符串:
var first:int = 24;
var second:int = 42;
// 结果为"2442"
var result:String = "" + first + second;
而且空字符串必须放在表达式的最前面,这样后面的都将被转换为字符串:
var first:int = 24;
var second:int = 42;
263. // 结果为"66"
var result:String = first + second + "";
另一个方法就是使用String( )转换函数(强制类型转换):
var first:int = 24;
var second:int = 42;
//结果为"2442"
var result:String = String( first ) + second;
而且要转换的必须是表达式的最前面的那个变量,下面的例子结果是不正确的:
var first:int = 24;
var second:int = 42;
var third:int = 21;
// 结果为"6621"
23
var result:String = first + second + String( third );
01
ye
另一个方法就是直接使用对象的toString( )方法,Number和int数据类型都有toString( )方法:
in
ix
var first:int = 24;
/l
et
var second:int = 42;
.n
var third:int = 21;
dn
cs
//结果为"244221"
/b !
g.
:/ 译
lo
var result:String = first.toString() + second + third;
tp 翻
ht 青
如果想在连接的表达式中进行加法运算,加法表达式必须放在括号中:
常
var first:int = 24;
var second:int = 42;
//结果为"There are 66 people"
var result:String = "There are " + ( first + second ) + " people";
通过+= 操作符可追加字符串:
var attendance:int = 24;
var example:String = "There are ";
example += attendance;
// 结果为"There are 24 people"
example += " people";
这种技术很友好好处,比如你的字符串过长,即可用这种方法把字符串分成多个字串,然后逐
264. 个追加上去,从代码上看更容易阅读:
var example:String = "This is the first sentence in a long paragraph of text.";
example += "By adding line by line to the string variable you make ";
example += "your code more readable.";
你也可以这样写:
var example:String = "This is the first sentence in a long paragraph of text. "
+ "By splitting the long string into smaller, more manageable pieces "
+ "and using the + operator, you can make your code more readable.";
字符串连接最常见的可能就是聊天程序了,比如聊天程序的历史纪录,聊天内容通过字符串连
接会不断地追加历史纪录中,例如:
// 下面的方法追加姓名和消息到聊天历史纪录中
private function updateChatHistory( message:String, username:String ):void {
23
// 历史纪录:history 变量
01
ye
_history += username + ":" + message + 'n';
};
in
ix
/l
通过String.concat( )方法也可以把新字符串追加到指定的字符串中,该方法不会影响源字符串,
et
而是返回一个新的字符串作为连接结果:
.n
dn
var original:String = "original string value.";
cs
/b !
g.
// 设置modified变量保存连接结果"original string value.now modified."
:/ 译
lo
tp 翻
// 源original保持不变
ht 青
常
var modified:String = original.concat( "now modified." );
266. 12.3. 插入特殊的空格字符
问题
我想在字符串中添加空格字符,比如制表符或新行字符
解决办法
使用特殊字符的转义序列
讨论
下面的表格列出了五种空格字符的转义字符。
可以在字符串里用这些转义序列,这在文本框中显示文本是很有用的:
var example:String = "thesetwordstaretseparatedtbyttabs";
// 结果为:these words are separated by tabs
空格字符 转义序列
23
01
ye
Newline n
in
ix
Tab t
/l
et
Backspace b
.n
dn
cs
Form feed f
/b !
g.
:/ 译
lo
Carriage return r
tp 翻
ht 青
常
var example:String = "thesenwordsnarenseparatednbynnewlines";
/* 结果为:
these
words
are
separated
by
newlines
*/
在ActionScript 3.0 已经取消早期版本所支持的newline变量,代替的是n转义序列。
// 编译器错误,须用n代替newline
var error:String = "two" + newline + "lines";
267. 在 Flash 里 newline, form feed, 和 carriage return 字符返回的结果一样, Flash 载入外部资源时 ,
当
有些可能含有 newline 字符,有些会是 form feeds,或者 carriage returns。
12.4. 搜索字串
问题
我想在字符串里找出指定的子串
解决办法
使用String类的indexOf( )或lastIndexOf( )方法
讨论
使用indexOf( )和lastIndexOf( )方法可检测出字符串中是否包含指定的子串,每个方法返回匹配子
23
串的起始索引。indexOf( )方法从左到右搜索,而lastIndexOf( )方法从右到左搜索,如果没找到则
01
返回-1。
ye
indexOf( )方法接受两个参数: in
ix
/l
substring
et
.n
指定要搜索的子串
dn
cs
startIndex
/b !
g.
可选参数,表示起始搜索位置,默认从0开始。
:/ 译
lo
tp 翻
如果要测试是否一个字符串包含另一个字符串,只需要传入一个参数给indexOf( )方法即可:
ht 青
常
var example:String = "This string contains the word cool twice. Very cool.";
// 获得子串"cool"在example中的索引
var index:int = example.indexOf( "cool" );
// 如果indexOf( )方法返回-1,表示没有找到"cool"
if ( index != -1 ) {
// 显示:"String contains word cool at index 30"
TRace( "String contains word cool at index " + index );
}
通过指定第二个参数指定搜索的起始位置,再次搜索,找出第二个匹配的字串位置:
var example:String = "This string contains the word cool twice. Very cool.";
// 得到第一个匹配的子串
var index:int = example.indexOf( "cool" );
268. if ( index != -1 ) {
// 显示: "String contains word cool at index 30"
trace( "String contains word cool at index " + index );
}
// 得到第二个匹配的子串"cool"
// 传递index + 1,以过滤掉第一个匹配子串,如果只传递index那么返回的仍是第一个子串位置
index = example.indexOf( "cool", index + 1 );
if ( index != -1 ) {
// 显示:"String contains word cool at index 47"
trace( "String contains word cool at index " + index );
}
在while循环语句中使用indexOf( )可找出所有匹配的子串位置,例如:
23
var example:String = "This string contains the word cool. Very cool. Yes, cool.";
01
ye
var index:int = -1;
in
ix
// 循环直到indexOf()返回-1
/l
et
while ( ( index = example.indexOf( "cool", index + 1 ) ) != -1 ) {
.n
/* 显示:
dn
cs
String contains word cool at index 30
/b !
g.
:/ 译
lo
String contains word cool at index 41
tp 翻
ht 青
String contains word cool at index 52
常
*/
trace( "String contains word cool at index " + index );
}
lastIndexOf( )方法与indexOf( )类似,只是它是从右边开始匹配搜索,返回第一个匹配的子串索引 。
lastIndexOf( )方法也接受两个参数意义和indexOf( )相同:
如果搜不到返回-1,默认从字符串的末尾(字符串长度)开始搜索。
var example:String = "This string contains the word cool twice. Very cool.";
//得到最后面的那个"cool"索引
var index:int = example.lastIndexOf( "cool" );
if ( index != -1 ) {
// 显示: "String contains word cool at index 47"
269. trace( "String contains word cool at index " + index );
}
// 得到第二个"cool"索引
// 通过传递index-1作为第二个参数
index = example.lastIndexOf( "cool", index - 1 );
if ( index != -1 ) {
// 显示: "String contains word cool at index 30" because the next to last
trace( "String contains word cool at index " + index );
}
和indexOf( )一样,在while循环里通过lastIndexOf( )找出所有匹配子串
var example:String = "This string contains the word cool. Very cool. Yes, cool.";
var index:int = example.length;
23
// 循环直到lastIndexOf( )返回-1.
01
ye
while ( ( index = example.lastIndexOf( "cool", index - 1 ) ) != -1 ) {
in
ix
/* 显示:
/l
et
String contains word cool at index 52
.n
String contains word cool at index 41
dn
cs
String contains word cool at index 30
/b !
g.
:/ 译
lo
*/
tp 翻
ht 青
trace( "String contains word cool at index " + index );
常
}
这里的代码和上面的indexOf( )很类似,只不过这里的index初始值为example.length而不是-1,因
为lastIndexOf( )是从右边开始搜索,第二个不同点是lastIndexOf( )第二个参数不是index+1。
indexOf( )和lastIndexOf( )方法都是区分大小写的,比如搜索"cool"就不能匹配"Cool"因为大小写
不同,要进行不区分大小写搜索,可使用toLowerCase( )方法进行转换。
var example:String = "Cool. This is a string with the word cool. It spells"
+ " cool as both cool (lowercase) and Cool (capitalized).";
var search:String = "cool";
//输出第一个匹配的"cool",结果为37,
//因为第一个"Cool"是大写
trace( example.indexOf( search ) );
// 经过小写转换后再次搜索匹配,返回结果为0
272. // 提取扩展名("jpg")
var extension:String = filename.substr( extensionIndex + 1, filename.length );
trace( "The file extension is " + extension );
也可用split( )方法:
var filename:String = "document.jpg";
var nameParts:Array = filename.split(".");
var extensionless:String = nameParts[0];
trace ("The filename is " + extensionless);
var extension:String = nameParts[1];
trace ("The file extension is " + extension);
这里有个问题,如果文件名本身也含有“.”符号,这么上面的两个方法提取的子串就会不正确 ,
这时候用lastIdexOf()方法比较好:
23
private function removeExtension( filename:String ):String {
01
// 找出最后面的“.”字符
ye
var extensionIndex:Number = filename.lastIndexOf( '.' );
in
ix
/l
if ( extensionIndex == -1 ) {
et
.n
// 如果没有“.”字符则直接返回filename
dn
cs
return filename;
/b !
g.
:/ 译
lo
} else {
tp 翻
ht 青
return filename.substr( 0, extensionIndex );
常
}
}
// 写在函数里可随时调用
private function extractExtension( filename:String ):String {
var extensionIndex:Number = filename.lastIndexOf( '.' );
if ( extensionIndex == -1 ) {
return "";
} else {
return filename.substr( extensionIndex + 1, filename.length );
}
}
273. 例子:
trace( removeExtension( "document.jpg" ) ); // 显示: document
trace( removeExtension( "document" ) ); // 显示: document
trace( removeExtension( "document.1.jpg" ) ); // 显示: document.1
trace( extractExtension( "document.jpg" ) ); // 显示: .jpg
trace( extractExtension( "document" ) ); // 显示nothing
trace( extractExtension( "document.1.jpg" ) ); // 显示: .jpg
12.6. 单词分析
23
问题
01
ye
我想处理字符串中的单词
in
ix
解决办法
/l
et
使用split( )方法
.n
讨论
dn
cs
split( )方法根据指定的分隔符把分离的子串存入数组,要把字符串分离出单词,可空格作为分隔
/b !
g.
:/ 译
lo
符。
tp 翻
ht 青
// 创建含有多个单词的字符串
常
var example:String = "This is a string of words";
//用空格作为分隔符分离字符串
var words:Array = example.split( " " );
//循环数组处理每个单词
for ( var i:int = 0; i < words.length; i++ ) {
/* 显示:
this
is
a
string
of
274. words
*/
trace( words[i] );
}
处理单词有很多方法,下面的完整例子演示分离字符串及显示单词:
package {
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
23
public class ActionScriptPoetry extends Sprite {
01
ye
public function ActionScriptPoetry( ) {
in
ix
stage.align = StageAlign.TOP_LEFT;
/l
et
stage.scaleMode = StageScaleMode.NO_SCALE;
.n
// 创建字符串
dn
cs
var example:String = "This is a string of ActionScript poetry words";
/b !
g.
:/ 译
lo
var words:Array = example.split(" ");
tp 翻
ht 青
var word:Sprite;
常
var wordText:TextField;
//遍历每个单词
for ( var i:int = 0; i < words.length; i++ ) {
// 为每个单词创建sprite并显示在屏幕上
word = new Sprite( );
addChild( word );
// 创建一个文本框
wordText = new TextField( );
word.addChild( wordText );
// 设置文本框大小,边框,背景颜色
wordText.autoSize = TextFieldAutoSize.LEFT; // Left-justify the text
275. wordText.border = true;
wordText.background = true;
wordText.selectable = false;
// 设置每个文本框显示一个单词
wordText.text = words[i];
// 点击sprite可拖动
word.addEventListener( MouseEvent.MOUSE_DOWN, handleDrag );
word.addEventListener( MouseEvent.MOUSE_UP, handleDrop );
// sprites的位置随机
var rx:Number = Math.random( ) * stage.stageWidth - word.width;
var ry:Number = Math.random( ) * stage.stageHeight - word.height;
word.x = rx;
23
word.y = ry;
01
ye
trace(word);
in
ix
}
/l
et
}
.n
// 当用户鼠标在单词上点击时调用该函数
dn
cs
private function handleDrag( event:MouseEvent ):void {
/b !
g.
:/ 译
lo
// 当前事件目标为TextField,因此它的父对象为sprite
tp 翻
ht 青
var word:Sprite = event.target.parent;
常
// 让单词置顶显示
setChildIndex( word, numChildren - 1 );
// 让单词可拖动
word.startDrag( );
}
// 当松开鼠标时调用该函数
private function handleDrop( event:MouseEvent ):void {
// 事件目标为TextField,其父对象为Sprite
var word:Sprite = event.target.parent;
// 停止拖动
word.stopDrag( );
276. }
}
}
上面的代码使用split( )函数以空格作为分隔符分离单词,在这里原始字符串还不包括标点符号,
如果含有标点符号或其他特殊符号则需用正则表达式作为split的参数过滤掉这些符号才能正确
分离出单词,比如下面的正则表达式匹配可以清除掉单词周围的标点符号/[^a-zA-Z0-9]+/.
// 创建有标点符号的字符串.
var example:String = "Here are some words. Also, here is some punctuation!";
// 用空格分离出单词,这些单词还含有标点符号
var words:Array = example.split( ' ' );
// 显示这些单词,他们还包含标点符号
for ( var i:int = 0; i < words.length; i++ ) {
/* Outputs:
23
01
Here
ye
are in
ix
/l
some
et
.n
words.
dn
Also,
cs
/b !
g.
here
:/ 译
lo
tp 翻
is
ht 青
常
some
punctuation!
*/
trace( words[i] );
}
// 使用正则表达式分离出正确的单词
words = example.split( /[^a-zA-Z0-9]+/ );
// 再次输出这些单词看看,现在已经没有标点符号了
for ( i = 0; i < words.length; i++ ) {
/* Outputs:
Here
are
277. some
words
Also
here
is
some
punctuation
*/
trace( words[i] );
}
关于正则表达式,请看第十三章。
23
01
ye
in
ix
12.7. 删除或替换字符或单词
/l
et
.n
问题
dn
cs
我要删除字符串上的字符或替换它
/b !
g.
:/ 译
lo
解决办法
tp 翻
ht 青
使用replace( )方法或split( )和join( )
常
讨论
ActionScript 3.0 提供一个新方法String.replace( ),用于字符替换,该方法接受两个参数:
pattern
查找或替换的字符串或正则表达式。
replace
替换的字符串,也可以是一个返回字符串的函数。
如果只提供一个参数时该方法有两个用途,这一节讨论字符串模式,正则表达式模式将在 13.4
节讨论。
这里有个简单的替换句子中的子串例子。replace( )方法返回替换掉的新字符串,原始字符串仍
未被修改。
var example:String = "This is a cool sentence."
//" is " 替换为" is not "
278. // 显示为:This is not a cool sentence.
trace( example.replace( " is ", " is not " ) );
在这个例子中替换字符串为" is ",周围是有空格的,这点很重要,否则的话句子中的"This is"也
会被替换为"This not is,",这就不是我们需要的结果了。
用字符串作为匹配模式有两个比较大的问题:替换次数和大小写问题,这些都要自己去处理,
如通过循环来替换掉所有匹配子串:
//创建字符串,一个原始的,一个为替换掉的
var example:String = "It's a bird, it's a plane, it's ActionScript Man!";
var replaced:String = example; // 先初始化为原始字符串
// 通过循环找出所有的"it's"子串,替换掉
while ( replaced.indexOf( "it's" ) != -1 ) {
// 替换"it's"为"it is".
replaced = replaced.replace( "it's", "it is" );
23
01
}
ye
// 解决大小写问题 in
ix
/l
replaced = replaced.replace( "It's", "It is" );
et
// 最后输出为:It is a bird, it is a plane, it is ActionScript Man!
.n
dn
trace( replaced );
cs
/b !
g.
split( )方法也可以被用来替换或删除字符串中的字符或单词。不像replace( )方法把字符串作为匹
:/ 译
lo
tp 翻
配模式,split是替换所有的符号,但是这两个方法都是忽略大小写问题。下面的例子用n替换
ht 青
掉<br>:
常
var example:String = "This is<br>a sentence<br>on 3 lines";
/* 显示:
This is
a sentence
on 3 lines
*/
trace( example.split( "<br>" ).join( 'n' ) );
split方法返回分离出的字符串数组, Array.join( )方法把数组元素有重新组成一个新字符串Split
而
方法删除了分隔符,而join( )方法正好相反,重新加入新的分隔符。
删除字符或单词就很简单了,只要替换为空串即可:
var example:String = "This is a cool sentence.";
279. // 删除单词"cool"
// 显示:This is a sentence.
trace( example.replace( "cool ", "" ) );
12.8. 每次只读取一个字符
问题
我想每次只读取字符串中的一个字符
解决办法
在for语句中使用String.charAt( )方法,也可以用String.split( )方法,以空字符串作为分隔符把所
23
有的字符分离出来作为数组,然后再用for语句遍历数组。
01
讨论
ye
最简单的方法就是在for循环中通过字符串的字符下标依次读取每个字符,下标范围为 0到
in
ix
/l
string.length-1,使用charAt( )方法即可读取字符进行处理。
et
.n
var example:String = "a string";
dn
cs
//循环遍历字符串中的每个字符
/b !
g.
for ( var i:int = 0; i < example.length; i++ ) {
:/ 译
lo
tp 翻
/* 每次输出一个字符:
ht 青
常
a
s
t
r
i
n
g
*/
trace( example.charAt( i ) );
}
使用split( ) 方法也可以达到同样的效果,这里我们使用空串作为split( ) 方法的分隔符参数。
var example:String = "a string";
280. var characters:Array = example.split( "" );
for ( var i:int = 0; i < characters.length; i++ ) {
/* a
s
t
r
i
n
g
*/
trace( characters[i] );
23
}
01
ye
两种方法都可以,不过如果你想要把字符排序一下的话最好用split方法,charAt( ) 方法就没那
么容易做到了:
in
ix
/l
var example:String = "a string";
et
.n
var characters:Array = example.split( "" );
dn
cs
//对字符数组进行排序
/b !
g.
:/ 译
lo
characters.sort( );
tp 翻
ht 青
for ( var i:int = 0; i < characters.length; i++) {
常
/* 显示:
a
g
i
n
r
s
t
*/
trace( characters[i] );
}
281. 如果想删除某些字符,split方法也比charAt()容易做到:
var example:String = "a string";
var characters:Array = example.split( "" );
for ( var i:Number = 0; i < characters.length; i++ ) {
// 删除所有“r”元素Remove all "r" elements from the array. Be sure to decrement i if an
// element is removed; otherwise, the next element is improperly skipped.
if ( characters[i] == "r") {
characters.splice( i, 1 );
i--;
}
}
// 显示: a sting
23
trace( characters.join( "" ) );
01
ye
in
ix
/l
et
.n
12.9. 大小写转换
dn
cs
/b !
g.
问题
:/ 译
lo
tp 翻
我想进行字符串的大小写转换以便执行大小写无关的比较运算
ht 青
常
解决办法
使用UpperCase( ) 和 toLowerCase( ) 方法。
讨论
toUpperCase( )和toLowerCase( ) 方法进行大小写处理后返回新的字符串,原始字符串还是未作
修改,在进行大小写无关性字符串搜索时这点是很有用的:
var example:String = "What case?";
// 显示: what case?
trace( example.toLowerCase( ) );
// 显示: WHAT CASE?
trace( example.toUpperCase( ) );
// 原始字符串还是未改变:What case?
trace( example );
282. 两个方法都返回新的字符串:
var example:String = example.toLowerCase( );
使用toLowerCase( )和toUpperCase( ) 可以把一个单词的首字母变成大写,其他小写。自定义类
ascb.util.StringUtilities.toInitialCap( )方法就是这个作用,此方法的代码:
public static function toInitialCap( original:String ):String {
return original.charAt( 0 ).toUpperCase( ) + original.substr( 1 ).toLowerCase( );
}
看下面的例子:
var example:String = "bRuCE";
trace( StringUtilities.toInitialCap( example ) ); // 显示: Bruce
toTitleCase( )方法把句子的所有单词首字母变成大写,方法定义如下:
public static function toTitleCase( original:String ):String {
23
var words:Array = original.split( " " );
01
ye
for (var i:int = 0; i < words.length; i++) {
words[i] = toInitialCap( words[i] );
in
ix
/l
}
et
.n
return ( words.join( " " ) );
dn
cs
}
/b !
g.
:/ 译
lo
看下面的例子:
tp 翻
ht 青
var example:String = "the actionScript cookbook";
常
// 显示: The ActionScript Cookbook
trace( StringUtilities.toTitleCase( example ) );
283. 12.10. 修正空格符
问题
我想修正字符串首尾的空格符。
解决办法
使用自定义类方法ascb.util.StringUtilities.trim( ),另外,如果你使用Flex 2 framework,则可以使
用mx.utils.StringUtil.trim( )静态方法。
讨论
字符串首尾的空格符总是让人很郁闷, 一般我们都需要处理掉。ActionScript 并没有提供现成的
trim( ) 实现,因此你必须自己实现了。
解决步骤如下:
把字符串分隔为字符数组。
删除开始部分的空格知道不是空格位置(tab, form feed, carriage return, newline, or space).
23
01
移除数组末尾的空格。
ye
使用join( )把字符数组连成新的字符串。
in
ix
/l
et
ascb.util.StringUtilities.trim( )方法正式按照上面的步骤进行处理的,代码定义如下,这里还定义
.n
了一个判断字符是否为空格的方法isWhitespace( ):
dn
cs
// 如果字符为空格则返回 true
/b !
g.
:/ 译
lo
public static function isWhitespace( ch:String ):Boolean {
tp 翻
ht 青
return ch == 'r' ||
常
ch == 'n' ||
ch == 'f' ||
ch == 't' ||
ch == ' ';
}
public static function trim( original:String ):String {
var characters:Array = original.split( "" );
for ( var i:int = 0; i < characters.length; i++ ) {
if ( isWhitespace( characters[i] ) ) {
characters.splice( i, 1 );
i--;
284. } else {
break;
}
}
for ( i = characters.length - 1; i >= 0; i-- ) {
if ( isWhitespace( characters[i] ) ) {
characters.splice( i, 1 );
} else {
break;
}
}
23
return characters.join("");
01
ye
}
in
ix
下面的例子演示了trim( )方法的使用:
/l
et
var example:String = "nrfta stringttnn";
.n
/* 调用trim( )方法之前显示如下:
dn
cs
this string value is:
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
a string
<end>
*/
trace( "this string value is: " + example + "<end>" );
example = StringUtilities.trim( example );
//显示: this string value is: a string<end>
trace( "this string value is: " + example + "<end>" );
Flex 2 framework 实现了trim方法,入下面的例子:
// 显示: a string<end>
trace( StringUtil.trim( "n rta stringttnn" ) + "<end>" );
285. 12.11. 反转字符串
问题
我要把字符串进行反转处理
解决办法
使用split( )方法创建字符数组,然后调用数组的reverse( )和join( )方法。
讨论
不管是单词还是字符都可以用同样的反转处理。唯一不同的是split()方法的分隔符。
处理步骤:
分隔字符串为数组,使用空格作为分隔符。
调用reverse( )方法进行反转。
使用join( )方法重新组织为字符串,当你反转单词时,使用空格作为连接符,当反转字符时使
用空字符串作为连接符:
23
01
ye
下面的代码演示了处理过程: in
ix
/l
var example:String = "hello dear reader";
et
.n
//分隔数组为单词数组.
dn
var words:Array = example.split( " " );
cs
/b !
g.
// 反转数组
:/ 译
lo
tp 翻
words.reverse( );
ht 青
常
// 使用空格连接数组元素为新的字符串
var exampleRevByWord:String = words.join( " " );
// 显示: reader dear hello
trace( exampleRevByWord );
// 分隔字符串为字符数组
var characters:Array = example.split( "" );
characters.reverse( );
var exampleRevByChar:String = characters.join( "" );
// 显示: redaer raed olleh
trace( exampleRevByChar );
287. // el baño?" has a ¿ at the beginning.
if ( example.charCodeAt( 0 ) == 191 ) {
trace( "The string "" + example + "" has a u00BF at the beginning." );
}
使用charCodeAt( )和fromCharCode( )方法可进行字符串的编码和解码处理。
下面的方法创建一个密码文字游戏,但是它仍然是不安全的,不能用于敏感型数据处理。
public static function encode( original:String ):String {
// The codeMap property is assigned to the StringUtilities class when the encode( )
// method is first run. Therefore, if no codeMap is yet defined, it needs
// to be created.
if ( codeMap == null ) {
// codeMap 属性是一个关联数组用于映射每个原始编码
23
codeMap = new Object( );
01
ye
// 创建编码为0到255的数组
in
ix
var originalMap:Array = new Array( );
/l
et
for ( var i:int = 0; i < 256 ; i++ ) {
.n
originalMap.push( i );
dn
cs
}
/b !
g.
:/ 译
lo
// 创建一个临时数组用于复制原始编码数组
tp 翻
ht 青
var tempChars:Array = originalMap.concat( );
常
// 遍历所有原始编码字符
for ( var i:int = 0; i < originalMap.length; i++ ) {
var randomIndex:int = Math.floor( Math.random( ) * ( tempChars.length - 1 ) );
codeMap[ originalMap[i] ] = tempChars[ randomIndex ];
tempChars.splice( randomIndex, 1 );
}
}
var characters:Array = original.split("");
for ( i = 0; i < characters.length; i++ ) {
characters[i] = String.fromCharCode( codeMap[ characters[i].charCodeAt( 0 ) ] );
}
288. }
public static function decode( encoded:String ):String {
var characters:Array = encoded.split( "" );
if ( reverseCodeMap == null ) {
reverseCodeMap = new Object( );
for ( var key in codeMap ) {
reverseCodesMap[ codeMap[key] ] = key;
}
}
for ( var i:int = 0; i < characters.length; i++ ) {
characters[i] = String.fromCharCode( reverseCodeMap[ characters[i].charCodeAt( 0 ) ] );
}
23
return characters.join( "" );
01
ye
}
in
ix
下面是具体使用方法:
/l
et
var example:String = "Peter Piper picked a peck of pickled peppers.";
.n
var encoded:String = StringUtilities.encode( example );
dn
cs
// 输出被编码的字符串,因为是随机产生的,每次输出都会不一样:
/b !
g.
:/ 译
lo
//
tp 翻
ht 青
æT#Tmïæ~*Tmï*~NcT­ï?ï*TNcï?2ï*~Nc?T­
常
ï*T**Tm:V
trace( encoded );
// 输出解码后的字符串
// 显示: Peter Piper picked a peck of pickled peppers.
trace( StringUtilities.decode( encoded ) );
289. 14.0. 简介
日期和时间对于很多ActionScript程序来说是很重要的,比如用于一些和时间相关的定时操作,
或者检测用户的登陆是否过期等。
在ActionScript内部是以毫秒的形式存储日期和时间的,但是很多编程语言的日期和时间是以秒
为单位的,这点需要注意。
另外,Date 类用于设置或获取日期和时间,或者直接通过其属性 fullYear, month 等,这些属性
的值也是以毫秒为单位的。
14.1. 获得当前日期和时间
23
问题
01
ye
我想知道当前日期和时间
in
ix
解决办法
/l
et
使用Date( ) 创建一个date对象,或者使用一个CGI脚本或其他服务端脚本返回服务器时间,然
.n
后根据返回值创建date对象
dn
cs
讨论
/b !
g.
:/ 译
lo
ActionScript计算出来的日期和时间是根据客户端计算机的日期和时间而得出的,因此如果客户
tp 翻
端的时间不正确,那么date对象也是不正确的。
ht 青
常
// 创建新的Date对象
var current:Date = new Date( );
// 显示客户端日期和时间
trace(current);
如果你连接上了互联网,Flash可以从服务器获得日期和时间,这项技术可以保证获得正确的日
期和时间(当然了你也可以故意把服务器时间设置错误,但是至少可以保证所有的客户端的时间
是一致的)。
一般从服务器获得时间的步骤如下:
在Web服务器上创建CGI脚本,输出时间(秒) 。
使用flash.net.URLLoader对象读取时间。
转换数值为number类型,乘以1000,再用这个数值重新构造一个date对象。
PHP是一种被广泛使用的脚本语言,用它输出服务器时间是很简单的:
<?php echo time( );?>
290. 如果你的服务器不支持PHP,或者说你更熟悉Perl (另一种脚本语言),输出也很简单:
#!/usr/local/bin/perl
print "Content-type:text/plainnn";
print time;
不管你使用什么服务端脚本,最后都要用ActionScript的flash.net.URLLoader对象载入服务器返回
的时间值。
package {
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.events.Event;
public class ServerDateTimeExample {
23
public function ServerDateTimeExample( ) {
01
// 下面的代码创建一个URLLoader对象,添加一个监听器,当数据载入
ye
// 完成时激活onDateTimeLoad( )方法
in
ix
/l
var loader:URLLoader = new URLLoader( );
et
.n
loader.addEventListener(Event.COMPLETE, onDateTimeLoad);
dn
cs
loader.load(new URLRequest("script.cgi"));
/b !
g.
:/ 译
lo
}
tp 翻
ht 青
private function onDateTimeLoad(event:Event):void {
常
var loader:URLLoader = URLLoader(event.target);
var data:int = parseInt(loader.data);
var current:Date = new Date(data * 1000);
trace(current);
}
}
291. 14.2. 获取时间值
问题
我要取得年月日,星期,小时,分,秒,毫秒等数值。
解决办法
是用fullYear, date, month, day, hours, minutes, seconds, milliseconds 等属性。
讨论
从Date对象的fullYear, date, month, day, hours, minutes, seconds, milliseconds属性中读取:
fullYear 属性返回4位数的年份值,如2010.
date 属性返回月天数,如1 到 31.
month 属性返回月份,如0 (1月) 到 11 (12月).
day 属性返回星期天数,如0 (星期天) 到 6 (星期六).
hours 属性返回小时数,如 0 到 23.
23
minutes 和 seconds 属性返回值为0 到 59.
01
ye
milliseconds 属性返回值为 0 到 999.
in
ix
/l
et
.n
dn
cs
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
292. 14.3. 获取星期天数和月份名称
问题
我想知道星期几或几月
解决办法
创建包含星期天数和月份的名称数组,然后使用数字的天数和月份来提取相应的数组元素值。
讨论
ActionScript的Date类提供了day和month属性,它们返回整数值如星期(0到6) 月 份 (0到11)。
,
但是如果想获得名称而不是数字的话就需要自己创建包含星期天数和月份的名称数组,或者使
用自定义类 ascb. util.DateFormat。它已经定义了DAYS, DAYS_ABBREVIATED, MONTHS, 和
MONTHS_ABBREVIATED 等数组,细节如下:
public static const DAYS:Array = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday"];
23
01
public static const DAYSABBREVIATED:Array = ["Sun", "Mon", "Tues", "Wed", "Thurs", "Fri",
ye
"Sat"];
in
ix
public static const MONTHS:Array = ["January", "February", "March", "April", "May", "June", "July",
/l
"August", "September", "October", "November", "December"];
et
.n
public static const MONTHSABBREVIATED:Array = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
dn
cs
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
/b !
g.
具体使用如下:
:/ 译
lo
tp 翻
var example:Date = new Date(2010, 3, 10);
ht 青
常
trace(DateFormat.DAYS[example.day]); // 显示: Saturday
trace(DateFormat.DAYSABBREVIATED[example.day]); // 显示: Sat
trace(DateFormat.MONTHS[example.month]); // 显示: April
trace(DateFormat.MONTHSABBREVIATED[example.month]); // 显示: Apr
293. 14.4. 格式化日期和时间
问题
我要自定义日期和时间的显示格式
解决办法
使用Date.toString( ), 或者自定义方法DateFormat.format( )
讨论
Date.toString( )方法返回Date对象的字符串类型数值,比如:
// 显示: Tue Jan 5 14:25:20 GMT-0800 2010
trace((new Date( )).toString( ));
如果忽略toString()方法,ActionScript 也会自动调用toString( )方法,结果是一样的,比如下
面的例子:
23
// 也显示: Tue Jan 5 14:25:20 GMT-0800 2010
01
ye
trace(new Date( ));
in
ix
Date类本身并没有内建一些格式化日期和时间的方法,必须自己去实现了,当然创建Date对象
/l
时可通过一些字符串的组合实现,看下面的例子:
et
.n
var example:Date = new Date(2010, 0, 5, 10, 25);
dn
cs
var formatted:String = (example.month + 1) + "/" + example.fullYear;
/b !
g.
:/ 译
lo
trace(formatted); // 显示: 1/2010
tp 翻
这样的话,每次格式化日期和时间都要自己写一大段代码了,ascb.util.DateFormat 对象提供了
ht 青
常
一个 format( ) 方法实现了类似功能,DateFormat 类是一个自定义类,专门设计来通过掩码格式
化日期和时间。掩码可以是任意字符组合。
当创建DateFormat对象时,传递一个掩码字符串作为参数,下面的例子创建了一个DateFormat
对象输出标准的美国日期格式:
var formatter:DateFormat = new DateFormat("m/d/Y");
一旦创建了DateFormat对象,调用format()方法,通过指定的掩码格式化Date实例:
var example:Date = new Date(2010, 0, 5, 10, 25);
var formatter:DateFormat = new DateFormat("m/d/Y");
trace(formatter.format(example)); // 显示: 01/05/2010
可以通过mask属性的get和set方法操作mask字符串,也就是说可以改变已存在的DateFormat 对
象的mask属性:
var example:Date = new Date(2010, 0, 5, 10, 25);
294. var formatter:DateFormat = new DateFormat("m/d/Y");
trace(formatter.format(example)); // 显示: 01/05/2010
formatter.mask = "m/d/Y h:i a";
trace(formatter.format(example)); // 显示: 01/05/2010 10:25 am
用单引号围绕的字符串可原样输出,这样就可以自定义格式化形式了:
var example:Date = new Date(2010, 0, 5, 10, 25);
var formatter:DateFormat = new DateFormat("m/d/Y at h:i a");
trace(formatter.format(example)); // 显示: 01/05/2010 am31 10:25 am
formatter.mask = "m/d/Y 'at' h:i a";
trace(formatter.format(example)); // 显示: 01/05/2010 at 10:25 am
23
01
ye
14.5. 格式化秒或毫秒为分或秒 ix
in
/l
et
问题
.n
我想把秒或毫秒格式化为分或秒。
dn
cs
解决办法
/b !
g.
:/ 译
lo
使用自定义类ascb.util.DateFormat.formatSeconds( ) 或 ascb.util.DateFormat.formatMilliseconds( )
tp 翻
ht 青
方法。
常
讨论
ActionScript里很多值都是以秒或毫秒形式存在的,例如声音长度单位是毫秒。但是在大多数情
况下我们希望显示单位为分或秒,这里我们提供了DateFormat实现了上述方法。
ascb.utils.DateFormat 类有两个方法把数字转换为 mm:ss 格式。 formatSeconds( ) 方法转换秒,
而 formatMilliseconds( ) 方法转换毫秒。
295. 14.6.DMYHMSM和毫秒之间的转换
14.6.DMYHMSM
问题
我想在DMYHMSM格式(天,月,年,小时,分,秒,毫秒等形式的时间) 和毫秒之间自由转换 。
解决办法
使用time属性
讨论
我们已经习惯时间和日期是以年月日的形式,例如时间10:25 a.m., Tuesday, January 5, 2010 很容
易理解,但是像ActionScript等一些语言是以毫秒为单位存储时间的,因此在显示给用户看之前
需要做个转换。
当我们用ActionScript构造一个日期时,可以以DMYHMSM 形式构造:
//构造一个日期 10:25 AM, Tuesday, January 5, 2010
23
var example:Date = new Date(2010, 0, 5, 10, 25);
01
ActionScript自动把日期转换为毫秒值,要获取这个值需要调用Date对象的time属性:
ye
// For Pacific Standard Time, displays: 1262715900000
in
ix
/l
trace(example.time);
et
.n
dn
cs
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
296. 14.7.使用 Timer
14.7. Timer(定时器)
问题
我想通过一定的间隔或一定的延时轮询某个方法
解决办法
使用flash.util.Timer 类
讨论
flash.util.Timer 类允许通过添加时间事件或延时来调用方法。通过 Timer构造器创建实例对象,
传递一个毫秒数字作为构造器参数作为间隔时间,下面的例子实例化一个Timer对象每隔1秒钟
发出事件信号:
var timer:Timer = new Timer(1000);
一旦创建了Timer实例,下一步必须添加一个事件监听器来处理发出的事件, Timer对象发出一
个flash.event.TimerEvent 事件,它是根据设置的间隔时间或延时时间定时发出。下面的代码定
23
01
义了一个事件监听器,调用onTimer( )方法作为处理函数:
ye
timer.addEventListener(TimerEvent.TIMER, onTimer); in
ix
function onTimer(event:TimerEvent):void {
/l
et
trace("on timer");
.n
dn
}
cs
/b !
g.
Timer 对象不会自动开始,必须调用start( )方法启动:
:/ 译
lo
tp 翻
timer.start( );
ht 青
默认情况下只有调用stop( )方法后才会停下来,不过另一种方法是传递给构造器第二个参数作为
常
运行次数,默认值为0即无限次,下面的例子设定定时器运行5次:
var timer:Timer = new Timer(1000, 5);
下面的代码设定定时器延时5秒执行deferredMethod( )方法:
var timer:Timer = new Timer(5000, 1);
timer.addEventListener(TimerEvent.TIMER, deferredMethod);
timer.start( );
297. 14.9. 字符串转换为日期
问题
我想通过字符串创建Date对象
解决办法
使用DateFormat对象的parse( )方法
讨论
ActionScript并没有提供现成的方法把字符串转换为日期,自定义类ascb.util.DateFormat的parse()
方法把传递进来的字符串参数转换为日期格式返回一个新的Date对象。
var formatter:DateFormat = new DateFormat("m/d/Y");
创建好DateFormat 实例,下一步就是调用 parse( )方法,传递一个字符串作为掩码,返回新的
Date实例:
23
// 先是: Sat May 1 00:00:00 GMT-0700 2010 (timezone offset may vary)
01
ye
trace(formatter.parse("05/01/2010"));
formatter.mask = "m/d/Y 'at' h:i a";
in
ix
/l
// 显示: Sat May 1 22:25:00 GMT-0700 2010 (timezone offset may vary)
et
.n
trace(formatter.parse("05/01/2010 at 10:25 PM"));
dn
cs
/b !
g.
:/ 译
lo
tp 翻
ht 青
15.0. 简介
常
在应用程序,游戏或Web站点上使用声音可以大大争强用户体验。在Flash IDE里通常是通过导
入声音库,把声音放入时间线帧,关联到电影剪辑等等。本章内容覆盖ActionScript 3.0 使用Sound
类及其相关类进行声音编程。
Sound类用于载入外部MP3文件,因为这文件并不是嵌入到swf内的,因此需要根据URL进行载
入,这要遵循域安全约束(第三章) 。
这一章涉及的类有:
Sound
SoundChannel
SoundLoaderContext
SoundMixer
SoundTransform
这些类都在 flash.media 包中,因此在写例子之前别忘了先引入 flash.media.Sound 包。
298. 15.1. 创建Sound
Sound
Sound对象及载入声音
问题
我想载入声音到SWF应用程序上。
解决办法
创建一个Sound对象,载入外部的声音文件
讨论
创建一个Sound对象很容易,这跟创建其他类实例差不多,首先记得先引入Sound类库:
import flash.media.Sound;
Var _sound :Sound = new Sound( );
接着我们需要一个声音文件,比如song.mp3,和swf文件放在服务器的同一个目录下。
要载入声音文件到Sound对象上,首先创建一个URLRequest对象(需要导入 flash.net.URLRequest):
23
01
soundFile = new URLRequest("song.mp3");
ye
_sound.load(soundFile); in
ix
可以把上面两句简写成下面那样:
/l
et
package {
.n
dn
import flash.display.Sprite;
cs
/b !
g.
import flash.media.Sound;
:/ 译
lo
tp 翻
import flash.net.URLRequest;
ht 青
常
public class LoadSoundExample extends Sprite {
private var _sound:Sound;
public function LoadSoundExample ( ) {
_sound = new Sound( );
_sound.load(new URLRequest("song.mp3"));
}
}
}
这个类有个属性:_sound,将通过它播放声音。注意上面的代码并没有真正开始播放,这仅仅
是做好准备工作,具体如何控制声音播放将在 第 15.2 章 介绍。
最简捷的写法就是直接把URLRequest传递给sound构造器:
public function LoadSoundExample( ) {
299. _sound = new Sound(new URLRequest("song.mp3"));
}
当传递URLRequest给构造器时,Sound会自动调用load( )方法载入声音数据。当只播放一首声音
时这种技巧很有效简洁。
否则当播放多首最好自己调用 load( )方法,最好的例子就是音乐播放器,当一首音乐被选择,
它的路径即被传递给 load( )方法,准备播放。
15.2. 开始和停止播放声音
问题
如何开始播放或停止播放声音。
23
01
解决办法
ye
使用play( )方法播放声音,使用close( )方法停止播放。 in
ix
讨论
/l
et
播放声音很简单,只要调用Sound对象的play( )方法即可,如:
.n
dn
_sound = new Sound(new URLRequest("song.mp3"));
cs
/b !
g.
_sound.play( );
:/ 译
lo
tp 翻
很简单吧,另外play( )方法有一些可选的参数,具体请看第15.1章 和 15.10章.。
ht 青
close( )方法即不是停止正在播放的声音,也不是停止声音流,当再次播放,用load()方法载入声
常
音前需要调用SoundChannel类。
301. 15.4. 声音的起始播放位置
问题
我不想从头播放声音而是从某个位置开始播放
解决办法
设置play( )方法的参数
讨论
很多情况下我们并不希望从头开始播放声音,换句话说,我们要剪掉前面一部分后开始播
放.Sound对象提供了这种能力让我们轻松做到这一点。
如果调用play( )方法不指定参数,即从头开始播放,如果传递一个毫秒为单位的可选参数就会从
指定的位置播放,如下面的例子在5.5秒开始播放:
_sound.play(5500);
23
利用这个特性我们可以做类似记录点的系统,让听众可以自由选择收听任何部分声音,看下面
01
的例子演示:
ye
package { in
ix
/l
import flash.display.Sprite;
et
.n
import flash.media.Sound;
dn
import flash.net.URLRequest;
cs
/b !
g.
public class CuePoints extends Sprite {
:/ 译
lo
tp 翻
private var _sound:Sound;
ht 青
常
private var _cuePoints:Array;
public function CuePoints( ) {
_cuePoints = [0, 10000, 30000, 68000, 120000];
_sound = new Sound(new URLRequest("song.mp3"));
// Play from the third cuepoint (30 seconds in)
playCuePoint(2);
}
public function playCuePoint(index:int):void {
_sound.play(_cuePoints[index]);
}
}
}
303. package {
import flash.display.Sprite;
import flash.media.Sound;
import flash.net.URLRequest;
import flash.events.Event;
public class ProgressBar extends Sprite {
private var _sound:Sound;
public function ProgressBar( ) {
addEventListener(Event.ENTER_FRAME, onEnterFrame);
_sound = new Sound(new URLRequest("song.mp3"));
_sound.play( );
}
23
public function onEnterFrame(event:Event):void
01
ye
{
in
ix
var barWidth:int = 200;
/l
et
var barHeight:int = 5;
.n
var loaded:int = _sound.bytesLoaded;
dn
cs
var total:int = _sound.bytesTotal;
/b !
g.
:/ 译
lo
if(total > 0) {
tp 翻
ht 青
// Draw a background bar
常
graphics.clear( );
graphics.beginFill(0xFFFFFF);
graphics.drawRect(10, 10, barWidth, barHeight);
graphics.endFill( );
// The percent of the sound that has loaded
var percent:Number = loaded / total;
// Draw a bar that represents the percent of
// the sound that has loaded
graphics.beginFill(0xCCCCCC);
graphics.drawRect(10, 10,
barWidth * percent, barHeight);
304. graphics.endFill( );
}
}
}
}
15.7. 读取声音文件的ID3
ID3
ID3标签数据
问题
我想访问正在播放的mp3文件的信息,比如歌曲名,艺术家,专辑,分类等等。
解决办法
23
01
读取Sound对象的id3属性
ye
讨论 in
ix
/l
MP3 文件大多包含一些如songname, artist, album, genre, year等元数据,不过这些并不是都有,
et
但大多数情况下都有songname和artist标签。
.n
dn
通过Sound对象的id3属性可获得这些数据。
cs
/b !
g.
这个属性其实是flash.media.ID3Info的类实例,它包含下列属性:
:/ 译
lo
tp 翻
album
ht 青
artist
常
comment
genre
songName
TRack
year
所以,想读取歌曲名,可这样写:
_sound.id3.songName
如果歌曲还没有下载到swf中,id3数据是不能够被访问的,那到底怎么知道id3数据已经下载了
呢,我们可以通过监听Sound对象的ID3事件,看下面的例子:
package {
import flash.display.Sprite;
import flash.media.Sound;
import flash.net.URLRequest;
305. import flash.events.Event;
import flash.text.TextField;
public class ID3Reader extends Sprite {
private var _sound:Sound;
public function ID3Reader ( ) {
_sound = new Sound(new URLRequest("song.mp3"));
_sound.addEventListener(Event.ID3, onID3);
_sound.play( );
}
public function onID3(event:Event):void {
// Create a text field and display it
var id3Display:TextField = new TextField( );
23
addChild(id3Display);
01
ye
id3Display.x = 10;
in
ix
id3Display.y = 20;
/l
et
id3Display.width = 200;
.n
id3Display.height = 200;
dn
cs
id3Display.background = true;
/b !
g.
:/ 译
lo
id3Display.multiline = true;
tp 翻
ht 青
id3Display.wordWrap = true;
常
// Add some info about the song to the text field
id3Display.text += _sound.id3.songName + "n";
id3Display.text += _sound.id3.artist + "n";
id3Display.text += _sound.id3.album + "n";
id3Display.text += _sound.id3.year + "n"; }
}
}
}
306. 15.8. 判定音乐是否播放完毕
问题
开始播放后,我想知道什么时候播放完毕
解决办法
监听soundComplete事件。
讨论
很多情况下我们需要知道什么时候音乐会播放完毕,例如,音乐播放器的播放列表,需要判定
是否播放完毕以便播放下一首音乐。
这一节我们将介绍 flash.media包中的 SoundChannel类。当我们调用 Sound对象的 play( ) 方 法 时 ,
它会返回一个SoundChannel对象,因此每一首正在播放的音乐都会产生一个SoundChannel对象,
这些声道合并并且最终输出。
当 声 音 播 放 完 毕 时 , 对 应 的 SoundChannel 对 象 会 发 出 soundComplete 事 件 , 就 是
23
01
flash.events.Event.SOUND_COMPLETE。
ye
下面的例子建立一个简单的播放列表: in
ix
package {
/l
et
import flash.display.Sprite;
.n
dn
import flash.media.Sound;
cs
/b !
g.
import flash.net.URLRequest;
:/ 译
lo
tp 翻
import flash.events.Event;
ht 青
常
import flash.media.SoundChannel;
public class PlayList extends Sprite {
private var _sound:Sound;
private var _channel:SoundChannel;
private var _playList:Array; // the list of songs
private var _index:int = 0; // the current song
public function PlayList( ) {
// 创建列表,开始播放
_playList = ["song1.mp3",
"song2.mp3",
"song3.mp3"];
playNextSong( );
307. }
private function playNextSong( ):void
{
// If there are still songs in the playlist
if(_index < _playList.length) {
// Create a new Sound object, load and play it
// _playList[_index] contains the name and path of
// the next song
_sound = new Sound( );
_sound.load(new URLRequest(_playList[_index]));
_channel = _sound.play( );
// Add the listener to the channel
23
_channel.addEventListener(Event.SOUND_COMPLETE,
01
ye
onComplete);
in
ix
// Increase the counter
/l
et
_index++;
.n
}
dn
cs
}
/b !
g.
:/ 译
lo
public function onComplete(event:Event):void
tp 翻
ht 青
{
常
playNextSong( );
}
}
}
这里变量_index 起始值为 0,则 _playList[index]正好等于"song.mp3",这将是第一首播放的歌曲,
接着_index 自增,当 soundComplete 事件触发时将会播放下一首歌,直到_index 大于_playList
的长度。
309. _channel = _sound.play( );
}
public function onEnterFrame(event:Event):void
{
var barWidth:int = 200;
var barHeight:int = 5;
var loaded:int = _sound.bytesLoaded;
var total:int = _sound.bytesTotal;
var length:int = _sound.length;
var position:int = _channel.position;
// Draw a background bar
graphics.clear( );
23
graphics.beginFill(0xFFFFFF);
01
ye
graphics.drawRect(10, 10, barWidth, barHeight);
in
ix
graphics.endFill( );
/l
et
if(total > 0) {
.n
// The percent of the sound that has loaded
dn
cs
var percentBuffered:Number = loaded / total;
/b !
g.
:/ 译
lo
// Draw a bar that represents the percent of
tp 翻
ht 青
// the sound that has loaded
常
graphics.beginFill(0xCCCCCC);
graphics.drawRect(10, 10,
barWidth * percentBuffered,
barHeight);
graphics.endFill( );
// Correct the sound length calculation
length /= percentBuffered;
// The percent of the sound that has played
var percentPlayed:Number = position / length;
// Draw a bar that represents the percent of
// the sound that has played
311. import flash.display.Sprite;
import flash.events.MouseEvent;
public class PlayPause extends Sprite {
private var _sound:Sound;
private var _channel:SoundChannel;
private var _playPauseButton:Sprite;
private var _playing:Boolean = false;
private var _position:int;
public function PlayPause( ) {
// Create sound and start it
_sound = new Sound(new URLRequest("song.mp3"));
_channel = _sound.play( );
23
_playing = true;
01
ye
// A sprite to use as a Play/Pause button
in
ix
_playPauseButton = new Sprite( );
/l
et
addChild(_playPauseButton);
.n
_playPauseButton.x = 10;
dn
cs
_playPauseButton.y = 20;
/b !
g.
:/ 译
lo
_playPauseButton.graphics.beginFill(0xcccccc);
tp 翻
ht 青
_playPauseButton.graphics.drawRect(0, 0, 20, 20);
常
_playPauseButton.addEventListener(MouseEvent.MOUSE_UP,
onPlayPause);
}
public function onPlayPause(event:MouseEvent):void {
// If playing, stop. Take note of position
if(_playing) {
_position = _channel.position;
_channel.stop( );
}
else {
// If not playing, re-start it at
312. // last known position
_channel = _sound.play(_position);
}
_playing = !_playing;
}
}
}
上面的代码创建了一个按钮,当点击按钮时,如果正在播放,则暂停并记录位置,再次点击按
钮时,继续从纪录的位置播放。
15.11. 获得声音的音量
23
01
ye
问题
in
ix
我想知道正在播放的声音的音量是多少
/l
et
解决办法
.n
通过SoundChannel.leftPeak and 和 SoundChannel.rightPeak 属性
dn
cs
讨论
/b !
g.
:/ 译
lo
任何声音,当在播放时产生强或弱的声波,我们称之为振幅,ActionScript 3.0 可获得一个立体
tp 翻
ht 青
声的左右声道的振幅,分别为SoundChannel的leftPeak和rightPeak属性。
常
它们的值范围是0.0到1.0,1.0表示最大的音量,具体的控制是通过 SoundTransorm对象(看第
15.14章).。
下面的例子创建两个矩形条用来显示两个声道的振幅:
package {
import flash.display.Sprite;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.net.URLRequest;
import flash.events.Event;
public class SoundLevels extends Sprite {
private var _sound:Sound;
private var _channel:SoundChannel;
313. public function SoundLevels( ) {
addEventListener(Event.ENTER_FRAME, onEnterFrame);
_sound = new Sound(new URLRequest("song.mp3"));
_channel = _sound.play( );
}
public function onEnterFrame(event:Event):void
{
var leftLevel:Number = _channel.leftPeak * 100;
var rightLevel:Number = _channel.rightPeak * 100;
graphics.clear( );
graphics.beginFill(0xcccccc);
graphics.drawRect(10, 10, leftLevel, 10);
23
graphics.endFill( );
01
ye
graphics.beginFill(0xcccccc);
in
ix
graphics.drawRect(10, 25, rightLevel, 10);
/l
et
graphics.endFill( );
.n
}
dn
cs
}
/b !
g.
:/ 译
lo
}
tp 翻
ht 青
常
314. 15.12. 停止播放所有的音乐
问题
我要停止所有当前正在播放的音乐
解决办法
使用SoundMixer的stopAll( )方法
讨论
当开始播放一个音乐时,它会产生一个SoundChannel对象,在一个swf里可以播放多个音乐,每
个音 乐 都 对 应 一 个 SoundChannel对象 , 声 音 某 些 方 面 由 Sound 对象 本 身 控 制 , 有 些 则 有
SoundChannel对象控制,最后所有正在播放的声音进行合成输出到扬声器上。这个重要的工作
由SoundMixer对象完成,它的属性和方法会影响所有正在播放的音乐。
其中一个方法就是stopAll( ),它会停止所有正在播放的声音。
虽然声音可以大大增强诸如游戏, Web站点的用户体验, 但是你也应该提供关闭音乐的功能 (毕
23
01
竟不是所有的人都喜爱听),SoundMixer类的静态方法stopAll( )正好用的上,因为是静态的,所
ye
以可以直接调用,像这样:
in
ix
public function stopSounds(event:Event):void {
/l
et
SoundMixer.stopAll( );
.n
dn
}
cs
/b !
g.
:/ 译
lo
tp 翻
ht 青
常
315. 15.13. 读取音乐的声谱
问题
我想显示出声音的波形图(声谱)
解决办法
使用SoundMixer.computeSpectrum( )填充一个字节数组,读取这个数组得到具体数据。
讨论
访问声谱数据是ActionScript 3.0 新增的特性之一,在早期的Flash版本时期,开发者也很想获得
这些数据,需要借助一些第三方工具,但是它们的实现过于复杂且效率低。现在这个功能已经
内置于SoundMixer类之中,再加上新的ByteArray类,用很少的代码即可显示数据。
首先让我们看一下 ByteArray这个类,它是ActionScript 3.0新增的专门用于处理二进制数据,经
过优化,效率高,它位于flash.utils包中。表面上看起来和一般的数组差不多,但是它的方法处
理数据比一般数组快的多。
23
01
要获得声谱数据,首先要创建一个空的ByteArray,像这样:
ye
var spectrum:ByteArray = new ByteArray( ); in
ix
ByteArray再作为SoundMixer.computeSpectrum( )方法的参数,这个方法获得声音的快照并计算出
/l
et
左右声道的波形,每个通道取256个值,范围在-1.0到1.0。再把数据存到ByteArray中。
.n
数据是准备好了,我们该怎么使用呢,我们需要循环遍历ByteArray 512次,调用getFloat( )方法,
dn
cs
前面256个值代表左声道,后256为右声道。
/b !
g.
:/ 译
lo
下面的例子通过BitmapData对象和setPixel32( )方法显示两个声道的波形:
tp 翻
ht 青
package {
常
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.net.URLRequest;
import flash.utils.ByteArray;
public class Spectrum extends Sprite {
private var _sound:Sound;
private var _channel:SoundChannel;
316. private var _spectrumGraph:BitmapData;
public function Spectrum( ) {
// Create bitmap for spectrum display
_spectrumGraph = new BitmapData(256, 60,true,0x00000000);
var bitmap:Bitmap = new Bitmap(_spectrumGraph);
addChild(bitmap);
bitmap.x = 10;
bitmap.y = 10;
addEventListener(Event.ENTER_FRAME, onEnterFrame);
_sound = new Sound(new URLRequest("song.mp3"));
_channel = _sound.play( );
}
23
public function onEnterFrame(event:Event):void
01
ye
{
in
ix
// Create the byte array and fill it with data
/l
et
var spectrum:ByteArray = new ByteArray( );
.n
SoundMixer.computeSpectrum(spectrum);
dn
cs
// Clear the bitmap
/b !
g.
:/ 译
lo
_spectrumGraph.fillRect(_spectrumGraph.rect,0x00000000);
tp 翻
ht 青
// Create the left channel visualization
常
for(var i:int=0;i<256;i++) {
_spectrumGraph.setPixel32(i,20 + spectrum.readFloat( ) * 20, 0xffffffff);
}
// Create the right channel visualization
for(var i:int=0;i<256;i++) {
_spectrumGraph.setPixel32(i,40 + spectrum.readFloat( ) * 20,0xffffffff);
}
}
}
}
317. 15.14. 改变声音的音量和平衡度
问题
我想改变音量或左右声道平衡
解决办法
创 建 SoundTransform 对 象 , 改 变 音 量 及 平 衡 度 , 把 该 对 象 赋 值 给 SoundChannel 对 象 的
soundTransform属性。
讨论
在以 前的版本中,可直接通过 Sound对象 改变音量及平衡度,现在这些被抽象出来形成
SoundTransform类。
SoundChannel对象有个soundTransform属性,它是SoundTransform的类实例。要改变音量或平衡
度,先创建SoundTransform对象,对其赋值,再赋给SoundChannel的soundTransform属性,下面
的例子设置音量为50%:
23
01
var _sound:Sound = new Sound(new URLRequest("song.mp3"));
ye
var channel:SoundChannel = _sound.play( ); in
ix
var transform:SoundTransform = new SoundTransform( );
/l
et
transform.volume = .5;
.n
dn
channel.soundTransform = transform;
cs
/b !
g.
音量的值范围为0.0到1.0 ,同样设置平衡度如下:
:/ 译
lo
tp 翻
var channel:SoundChannel = _sound.play( );
ht 青
常
var transform:SoundTransform = new SoundTransform( );
transform.pan = -1.0;
channel.soundTransform = transform;
值范围为-1.0到1.0 。
还可以把这些值直接传给SoundTransform的构造函数,如:
var channel:SoundChannel = _sound.play( );
var transform:SoundTransform = new SoundTransform(.5, -1.0);
channel.soundTransform = transform;
318. 15.15. 编写一个音乐程序
问题
我想写一个全功能的音乐程序,比如一个 MP3 播放器
解决办法
利用前面讲到的知识即可做到
讨论
这一节将整合前面的技术到一个程序上:
package {
import flash.display.Sprite;
import flash.display.Stage;
import flash.display.StageAlign;
23
01
import flash.display.StageScaleMode;
ye
import flash.text.TextField; in
ix
import flash.events.Event;
/l
et
import flash.events.MouseEvent;
.n
dn
import flash.events.TimerEvent;
cs
/b !
g.
import flash.media.Sound;
:/ 译
lo
tp 翻
import flash.media.SoundChannel;
ht 青
常
import flash.media.SoundTransform;
import flash.net.URLRequest;
import flash.text.TextFormat;
import flash.utils.Timer;
public class CookBookPlayer extends Sprite {
private var _channel:SoundChannel;
private var _displayText:TextField;
private var _sound:Sound;
private var _panControl:PanControl;
private var _playing:Boolean = false;
private var _playPauseButton:Sprite;
private var _position:int = 0;
319. private var _spectrumGraph:SpectrumGraph;
private var _volumeControl:VolumeControl;
public function CookBookPlayer( ) {
// Stage alignment
stage.scaleMode = flash.display.StageScaleMode.NO_SCALE;
stage.align = flash.display.StageAlign.TOP_LEFT;
// Enter frame listener
var timer:Timer = new Timer(20);
timer.addEventListener(TimerEvent.TIMER, onTimer);
timer.start( );
_playing = true;
// Display a text field
23
_displayText = new TextField( );
01
ye
addChild(_displayText);
in
ix
_displayText.x = 10;
/l
et
_displayText.y = 17;
.n
_displayText.width = 256;
dn
cs
_displayText.height = 14;
/b !
g.
:/ 译
lo
// Create a sound object
tp 翻
ht 青
_sound = new Sound(new URLRequest(""http://www.rightactionscript.com
常
samplefiles/sample.mp3""));
_sound.addEventListener(Event.ID3, onID3);
_channel = _sound.play( );
// Create a bitmap for spectrum display
_spectrumGraph = new SpectrumGraph( );
_spectrumGraph.x = 10;
_spectrumGraph.y = 33;
addChild(_spectrumGraph);
// Create the Play and Pause buttons
_playPauseButton = new PlayButton( );
_playPauseButton.x = 10;
320. _playPauseButton.y = 68;
addChild(_playPauseButton);
_playPauseButton.addEventListener(MouseEvent.MOUSE_UP, onPlayPause);
// Create volume and pan controls
_volumeControl = new VolumeControl( );
_volumeControl.x = 45;
_volumeControl.y = 68;
addChild(_volumeControl);
_volumeControl.addEventListener(Event.CHANGE,onTransform);
_panControl = new PanControl( );
_panControl.x = 164;
_panControl.y = 68;
23
addChild(_panControl);
01
ye
_panControl.addEventListener(Event.CHANGE, onTransform);
in
ix
}
/l
et
public function onTransform(event:Event):void
.n
{
dn
cs
// Get volume and pan data from controls
/b !
g.
:/ 译
lo
// and apply to a new SoundTransform object
tp 翻
ht 青
_channel.soundTransform = new SoundTransform(_volumeControl.volume,
常
_panControl.pan);
}
public function onPlayPause(event:MouseEvent):void
{
// If playing, stop and record that position
if(_playing) {
_position = _channel.position;
_channel.stop( );
}
else {
// Else, restart at the saved position
321. _channel = _sound.play(_position);
}
_playing = !_playing;
}
public function onID3(event:Event):void {
// Display selected id3 tags in the text field
_displayText.text = _sound.id3.artist + " : " +
_sound.id3.songName;
_displayText.setTextFormat(
new TextFormat("_typewriter", 8, 0));
}
public function onTimer(event:TimerEvent):void {
23
var barWidth:int = 256;
01
ye
var barHeight:int = 5;
in
ix
var loaded:int = _sound.bytesLoaded;
/l
et
var total:int = _sound.bytesTotal;
.n
var length:int = _sound.length;
dn
cs
var position:int = _channel.position;
/b !
g.
:/ 译
lo
// Draw a background bar
tp 翻
ht 青
graphics.clear( );
常
graphics.beginFill(0xFFFFFF);
graphics.drawRect(10, 10, barWidth, barHeight);
graphics.endFill( );
if(total > 0) {
// The percent of the sound that has loaded
var percentBuffered:Number = loaded / total;
// Draw a bar that represents the percent of
// the sound that has loaded
graphics.beginFill(0xCCCCCC);
graphics.drawRect(10, 10, barWidth * percentBuffered,barHeight);
graphics.endFill( );
322. // Correct the sound length calculation
length /= percentBuffered;
// The percent of the sound that has played
var percentPlayed:Number = position / length;
// Draw a bar that represents the percent of
// the sound that has played
graphics.beginFill(0x666666);
graphics.drawRect(10, 10,barWidth * percentPlayed,barHeight);
graphics.endFill( );
_spectrumGraph.update( );
}
}
23
}
01
ye
}
in
ix
// "helper classes"
/l
et
// (This is an outside package, but it's available to classes
.n
// in the same file)
dn
cs
import flash.display.Bitmap;
/b !
g.
:/ 译
lo
import flash.display.BitmapData;
tp 翻
ht 青
import flash.display.Sprite;
常
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filters.DropShadowFilter;
import flash.geom.Rectangle;
import flash.media.SoundMixer;
import flash.utils.ByteArray;
class PlayButton extends Sprite {
public function PlayButton( ) {
// Draw the Play/Pause graphic
graphics.beginFill(0xcccccc);
graphics.drawRoundRect(0, 0, 20, 16, 4, 4);
323. graphics.endFill( );
graphics.beginFill(0x333333);
graphics.moveTo(4, 4);
graphics.lineTo(8, 8);
graphics.lineTo(4, 12);
graphics.lineTo(4, 4);
graphics.drawRect(10, 4, 2, 8);
graphics.drawRect(14, 4, 2, 8);
graphics.endFill( );
}
}
class SpectrumGraph extends Sprite {
23
private var _spectrumBMP:BitmapData;
01
ye
public function SpectrumGraph( )
in
ix
{
/l
et
// Bitmap to draw spectrum data in
.n
_spectrumBMP = new BitmapData(256, 30,true, 0x00000000);
dn
cs
var bitmap:Bitmap = new Bitmap(_spectrumBMP);
/b !
g.
:/ 译
lo
bitmap.filters = [new DropShadowFilter(3, 45, 0, 1,3, 2, .3, 3)];
tp 翻
ht 青
addChild(bitmap);
常
}
public function update( ):void
{
// Get spectrum data
var spectrum:ByteArray = new ByteArray( );
SoundMixer.computeSpectrum(spectrum);
// Draw to bitmap
_spectrumBMP.fillRect(_spectrumBMP.rect, 0xff666666);
_spectrumBMP.fillRect(new Rectangle(1, 1, 254, 28), 0x00000000);
for(var i:int=0;i<256;i++) {
_spectrumBMP.setPixel32(i,
324. 10 + spectrum.readFloat( ) * 10,
0xff000000);
}
for(var i:int=0;i<256;i++) {
_spectrumBMP.setPixel32(i,
20 + spectrum.readFloat( ) * 10,
0xff000000);
}
}
}
class VolumeControl extends Sprite {
public var volume:Number = 1.0;
23
public function VolumeControl( )
01
ye
{
in
ix
addEventListener(MouseEvent.CLICK, onClick);
/l
et
draw( );
.n
}
dn
cs
public function onClick(event:MouseEvent):void
/b !
g.
:/ 译
lo
{
tp 翻
ht 青
// When user clicks the bar, set the volume
常
volume = event.localX / 100;
draw( );
dispatchEvent(new Event(Event.CHANGE));
}
private function draw( ):void {
// Draw a bar and the current volume position
graphics.beginFill(0xcccccc);
graphics.drawRect(0, 0, 102, 16);
graphics.endFill( );
graphics.beginFill(0x000000);
graphics.drawRect(volume * 100, 0, 2, 16);
325. }
}
class PanControl extends Sprite {
public var pan:Number = 0;
public function PanControl( )
{
addEventListener(MouseEvent.CLICK, onClick);
draw( );
}
public function onClick(event:MouseEvent):void
{
// When the user clicks bar, set pan
23
pan = event.localX / 50 - 1;
01
ye
draw( );
in
ix
dispatchEvent(new Event(Event.CHANGE));
/l
et
}
.n
private function draw( ):void {
dn
cs
// Draw the bar and current pan position
/b !
g.
:/ 译
lo
graphics.beginFill(0xcccccc);
tp 翻
ht 青
graphics.drawRect(0, 0, 102, 16);
常
graphics.endFill( );
graphics.beginFill(0x000000);
graphics.drawRect(50 + pan * 50, 0, 2, 16);
}
}
326. 16.0. 简介
Flash播放器是有能力回放视频的,虽然也可以把视频内容嵌入到 swf文件中,但大部分Flash视
频内容都是以.flv格式存储的,通过ActionScript在运行时载入到Flash播放器,这样swf文件更小,
更利于管理视频内容。
Flash 视频载入有两种形式:渐进式下载和流下载。flv 流视频需要流服务器,比如 Flash Media
Server。相反,渐进式下载不需要额外的软件,不过 ActionScript 处理这两种的方式是相同的,
这一章将讨论如何播放 flv 视频。
16.1. 载入并播放视频
23
问题
01
ye
我想渐进式下载视频并播放
in
问题
ix
/l
使用NetStream对象载入并播放视频,使用Video对象显示视频内容
et
.n
讨论
dn
cs
ActionScript 3.0 需要多个类同时工作来载入和回放Flash视频。你必须使用NetStream对象载入视
/b !
g.
频并控制回放,但是NetStream类只关心如何读取数据,至于这些数据是什么内容并不知道,因
:/ 译
lo
tp 翻
此就需要Video对象,Video对象得到NetStream的数据并显示到屏幕上。
ht 青
常
NetStream构造函数需要一个NetConnection对象作为参数,NetConnection对象关联将要播放的视
频。当Flash视频来自于Flash Communication Server或Flash Media Server,NetConnection对象指向
服务器, 但是如果是下载渐进式视频内容则NetConnection对象的连接字符串为null。下面的代码
构造一个NetConnection对象并初始化, 用来下载渐进式视频, 注意先引入flash.net.NetConnection
类:
var videoConnection:NetConnection = new NetConnection( );
videoConnection.connect(null);
一旦创建了NetConnection对象并调用了connect( )方法,再构造NetStream对象,把NetConnection
对象引用传给 NetStream构造器作为参数。下面的代码构造了个 NetStream对象(先引入imported
flash.net.NetStream):
var videoStream:NetStream = new NetStream(videoConnection);
创建好NetStream对象后,创建Video对象,并把两者关联起来。
flash.media.Video类是个可视化对象,因此可以添加到显示列表,下面的代码构造了一个 Video
对象并添加到显示列表:
327. var video:Video = new Video( );
addChild(video);
接着调用Video对象的attachNetStream( )方法,和NetStream对象关联起来:
video.attachNetStream(videoStream);
NetStream类定义了一个play( )方法用于载入并开始播放Flash视频,参数可以是相对或绝对URL。
下面的代码表示载入并播放名为example.flv的视频:
videoStream.play("example.flv");
如果.flv文件和swf文件在同一个域里,play( )调用不会被Flash Player安全机制拒绝,但是如果在
不同的域时就需要一个安全策略文件。
当缓冲足够时,视频会被自动播放,我们可控制缓冲和监视载入进度,这些将在第16.7章.讨论。
如果 flv 文件是嵌入进来的元数据,则需要处理元事件,这些将在第 16.4 章.讨论。 另外如果
flv 文件包含提示点,则需要处理提示点事件,这些将在第 16.8 章.讨论。
23
01
ye
16.2. 控制视频声音 in
ix
/l
et
问题
.n
dn
我想控制视频声音的音量及平衡
cs
/b !
g.
解决办法
:/ 译
lo
tp 翻
使用NetStream对象的soundTransform 属性
ht 青
常
讨论
如果 Flash 视频有音轨,则声音部分会自动随着视频播放。如果想控制音量及平衡度,则需要
访问 NetStream 对象的 soundTransform 属性,来获得一个 SoundTransform 对象的一个引用。更
多细节请看第 15.14 章.
338. public class BandwidthTest extends EventDispatcher {
private var _downloadCount:uint;
private var _bandwidthTests:Array;
private var _detectedBandwidth:Number;
private var _startTime:uint;
public function get detectedBandwidth( ):Number {
return _detectedBandwidth;
}
public function BandwidthTest( ) {
_downloadCount = 0;
_bandwidthTests = new Array( );
}
23
// Run the bandwidth test.
01
ye
public function test( ):void {
in
ix
// Use a URLLoader to load the data.
/l
et
var loader:URLLoader = new URLLoader( );
.n
// Use a URL with a unique query string to ensure the data is
dn
cs
// loaded from the server and not from browser cache.
/b !
g.
:/ 译
lo
var request:URLRequest = new URLRequest("bandwidthtestimage.jpg?unique="
tp 翻
ht 青
+ (new Date( )).getTime( ));
常
loader.load(request);
loader.addEventListener(Event.OPEN, onStart);
loader.addEventListener(Event.COMPLETE, onLoad);
}
// When the file starts to download get the current timer value.
private function onStart(event:Event):void {
_startTime = getTimer( );
}
private function onLoad(event:Event):void {
// The download time is the timer value when the file has downloaded
// minus the timer value when the value started downloading. Then
339. // divide by 1000 to convert from milliseconds to seconds.
var downloadTime:Number = (getTimer( ) - _startTime) / 1000;
_downloadCount++;
// Convert from bytes to kilobits.
var kilobits:Number = event.target.bytesTotal / 1000 * 8;
// Divide the kilobits by the download time.
var kbps:Number = kilobits / downloadTime;
// Add the test value to the array.
_bandwidthTests.push(kbps);
if(_downloadCount == 1) {
// If it's only run one test then run the second.
test( );
23
}
01
ye
else if(_downloadCount == 2) {
in
ix
// If it's run two tests then determine the margin between the
/l
et
// first two tests.
.n
// If the margin is small (in this example, less than 50 kbps)
dn
cs
// then dispatch a complete event. If not run a test.
/b !
g.
:/ 译
lo
if(Math.abs(_bandwidthTests[0] - _bandwidthTests[1]) < 50) {
tp 翻
ht 青
dispatchCompleteEvent( );
常
}
else {
test( );
}
}
else {
// Following the third test dispatch a complete event.
dispatchCompleteEvent( );
}
}
private function dispatchCompleteEvent( ):void {
340. // Determine the avarage bandwidth detection value.
_detectedBandwidth = 0;
var i:uint;
for(i = 0; i < _bandwidthTests.length; i++) {
_detectedBandwidth += _bandwidthTests[i];
}
_detectedBandwidth /= _downloadCount;
// Dispatch a complete event.
dispatchEvent(new Event(Event.COMPLETE));
}
}
}
23
可以用上面的代码进行带宽测试,代码如下:
01
ye
var bandwidthTester:BandwidthTest = new BandwidthTest( );
in
ix
bandwidthTester.addEventListener(Event.COMPLETE, onBandwidthTest);
/l
et
bandwidthTester.test( );
.n
当complete事件触发时,访问detectedBandwidth属性得到带宽值
dn
cs
private function onBandwidthTest(event:Event):void {
/b !
g.
:/ 译
lo
trace(event.target.detectedBandwidth);
tp 翻
ht 青
}
常
341. 17.0. 简介
电影剪辑在运行时,大多数数据都存储在内容中,一旦电影剪辑关闭,那么这些数据也同时从
内存中清除掉,如果想存储数据或者让客户端的两个电影剪辑共享数据该怎么办呢,要想办法
把数据存储在Flash播放器外面。
ActionScript中,SharedObject类实现了客户端机器数据的持久性存储。有两种类型的共享对象:
本地和远程,这章集中讨论local shared objects (LSOs).
Local shared objects 很类似于浏览器中 cookies,LSOs 的功能也和 cookies 很类似,如存储用户
登陆网站的用户名,这样不必每次登陆都要输入用户名了,不过 LSOs 的功能不仅限于此,它
比 cookies 要强大的多,他们不必在客户端和服务器端进行传输,且都是以 ActionScript 数据类
型形式存储。与此对应的服务端有 remote shared objects (RSOs), LSOs 不需要在客户端和服务
端额外的软件即可工作。
23
01
ye
in
17.1. 创建,打开 Local Shared Object
ix
/l
et
.n
问题
dn
cs
我想当访问swf过程中存储一些信息
/b !
g.
:/ 译
lo
解决办法
tp 翻
ht 青
使用LSO.
常
讨论
如同这章的简介里描述的那样,Flash的LSOs 就如同Web浏览器中的cookies,它们被一些开发
者称为“超级cookies”,因为LSOs可以存储大量数据,且存储和读取的都是原生的ActionScript
数据类型。
LSOs 默认的空间大小100 KB, 用户可以通过Flash Player's Settings Manager控制LSOs的使用空
间大小, 来严格限制被使用的空间。 存储在客户端的LSO文件是一种二进制文件, 扩展名为.sol。
同一个域的Flash电影通过flash.net.SharedObject类读写.sol文件。
当.sol文件创建后,被放置在Flash播放器对应的应用程序数据目录,以Windows为例,目录为
C:Documents and Settings[ username ] Application DataMacromediaFlash
Player#SharedObjects [ random character directory name ] , 在 Mac OS X 上 , 目 录 为
/Users/[ username ] /Library/Preferences/Macromedia/Flash Player/#SharedObject/ [ random
character directory name ]。随机字符目录命名是为了安全考虑。 一些恶意的swf文件可能试图
猜测shared object(共享对象)的名称或目录,以便从文件系统中读取LSO,进而提升访问权限 。
因此必须把路径随机化,这样猜路径几乎是不太可能了。
342. LSOs 的静态方法 getLocal( ) 用于创建或打开共享对象,它至少需要一个参数指明共享对象名
称:
var example:SharedObject = SharedObject.getLocal( "example" );
getLocal( ) 方法首先试图定位找到 LSO,如果找不到则根据这个名字创建新的,否则则返回
SharedObject 实例。
17.2. 写入数据到共享对象上
问题
我想添加数据到LSO上.
解决办法
23
给共享对象的data对象添加属性值
01
讨论
ye
共享对象(Shared objects)有个内建的属性data,data属性类型为object,因此可以添加任何信息
in
ix
/l
上去:
et
.n
// 存储username值给example共享对象
dn
cs
example.data.username = "Darron";
/b !
g.
和早期版本的ActionScript不同,现在不能直接把属性值赋值给共享对象本身了,如果这样做了
:/ 译
lo
tp 翻
编译器会直接报错:
ht 青
常
example.variable = "This will cause a compile-time error.";
另外,直接把值赋值给data属性也不正确:
example.data = "This will cause another compile-time error.";
正确的写法应该这样:
example.data.variable = "This is the correct way to store data.";
可以直接存储ActionScript原生数据类型:
example.data.exampleArray = new Array( "a", "b", "c" );
example.data.exampleDate = new Date();
example.data.exampleObject = { key1: "example", key2: -12, key3: null };
但需要注意的是,不能存储可视化对象(例如 MovieClips,Sprite,Buttons,TextFields)
343. 17.3. 保存本地共享对象
问题
我想保存LSO到客户机上
解决办法
使用SharedObject.flush( )方法
讨论
以下几种情况会导致Flash自动试图保存LSO数据到硬盘上:当Flash播放器关闭时,当共享对象
被垃圾回收时,当调用 SharedObject.clear( )方法时。但是自动保存功能并不很实用,因为还有
很多原因需要及时保存共享对象数据,因此我们可以显式调用SharedObject.flush( )方法:
var flushResult:String = example.flush( );
flush( )方法有个可选的参数用于指定最小的硬盘空间,单位为字节,默认为0,指用最小的空间
正好存储本地共享对象。
23
01
当flush( )方法触发时,它试图把数据写到客户端上,调用结果有三种:
ye
如果用户拒绝存储或Flash播放器因某种原因导致存储失败,该方法会抛出一个Error。 in
ix
如果本地存储空间不够导致数据不能保存,该方法返回SharedObjectFlushStatus.FLUSHED。
/l
et
如果用户没有分配足够的空间,该方法返回SharedObjectFlushStatus.PENDING。
.n
dn
cs
三种情况中, flush( )方法返回SharedObjectFlushStatus.PENDING常量时,
当 用户可以选择授权或
/b !
g.
:/ 译
lo
拒绝保存数据,当用户做出选择后,netStatus事件被激活,需要定义一个事件处理函数,当事
tp 翻
件处理函数被触发时,传递进一个类型为flash.events.NetStatusEvent的事件,检查info.code 属性
ht 青
值判断用户是同意(SharedObject.Flush.Success)还是拒绝(SharedObject.Flush.Failed)
常
这里有个例子调用flush( )保存数据,处理可能返回的结果:
var example:SharedObject = SharedObject.getLocal( "example" );
example.data.someData = "a value";
try {
var flushResult:String = example.flush( );
// If the flush operation is pending, add an event handler for
// netStatus to determine if the user grants or denies access.
// Otherwise, just check the result.
if ( flushResult == SharedObjectFlushStatus.PENDING ) {
// Add an event handler for netStatus so we can check if the user
// granted enough disk space to save the shared object. Invoke
344. // the onStatus method when the netStatus event is raised.
example.addEventListener( NetStatusEvent.NET_STATUS, onStatus );
} else if ( flushResult == SharedObjectFlushStatus.FLUSHED ) {
// Saved successfully. Place any code here that you want to
// execute after the data was successfully saved.
}
} catch ( e:Error ) {
// This means the user has the local storage settings to 'Never.'
// If it is important to save your data, you may want to alert the
// user here. Also, if you want to make it easy for the user to change
// his settings, you can open the local storage tab of the Player
// Settings dialog box with the following code:
23
// Security.showSettings( SecurityPanel.LOCAL_STORAGE );.
01
ye
}
in
ix
// Define the onStatus() function to handle the shared object's
/l
et
// status event that is raised after the user makes a selection from
.n
// the prompt that occurs when flush( ) returns "pending."
dn
cs
function onStatus( event:NetStatusEvent ):void {
/b !
g.
:/ 译
lo
if ( event.info.code == "SharedObject.Flush.Success" ) {
tp 翻
ht 青
// If the event.info.code property is "SharedObject.Flush.Success",
常
// it means the user granted access. Place any code here that
// you want to execute when the user grants access.
} else if ( event.info.code == "SharedObject.Flush.Failed" ) {
// If the event.info.code property is "SharedObject.Flush.Failed", it
// means the user denied access. Place any code here that you
// want to execute when the user denies access.
}
// Remove the event listener now since we only needed to listen once
example.removeEventListener( NetStatusEvent.NET_STATUS, onStatus );
};
如果确切知道存储数据的大小,可直接给flush( )传参数:
345. // Request 500 KB of space for the shared object.
var flashResult:String = example.flush( 500 * 1024 );
17.4. 从共享对象中读取数据
问题
我想读取先前写入到LSO的数据
解决办法
读取共享对象的data属性中的值
讨论
在客户端读取这些内容很简单,这些持久性数据都保存在共享对象的 data属性里,因此像下面
23
的语句这样读就可以了:
01
ye
// Read the value of exampleProperty from the shared object,
in
ix
// example, and display it in the Output window.
/l
et
trace( example.data.exampleProperty );
.n
通过读写数据,我们可以判定用户是不是头一次访问swf文件:
dn
cs
// Create a shared object and store some data in it
/b !
g.
:/ 译
lo
var example:SharedObject = SharedObject.getLocal( "example" );
tp 翻
ht 青
if ( example.data.previouslyViewed ) {
常
// The user has already viewed the .swf file before, perhaps
// we skip an introductory help screen here.
} else {
// This is the first time the user is viewing the .swf file
// because previouslyViewed has not yet been set to true.
// Set previouslyViewed to true so that the next time this
// code is run we know the user has been here before.
example.data.previouslyViewed = true;
example.flush( );
}
346. 17.5. 删除共享对象中保存的数据
问题
我想删除共享对象中的某个属性值或者干脆删除整个共享对象
解决办法
使用delete删除共享对象的data属性中的值,或使用clear( )方法清除整个共享对象
讨论
删除共享对象中的数据是很简单的,但是要注意方法,在ActionScript中我们经常看到删除对象
或数组只要赋值为null或undefined即可,但是对于共享对象这样做却不行:
// 试图删除共享对象example的someVariable 值
// 这语句编译通过,但是并不是我们所期望的
example.data.someVariable = null;
23
因为共享对象中null和undefined都是有效的值,因此上面的代码并没有删除someVariable属 性 ,
01
只不过设置为null而已,正确的方法是使用delete命令,如:
ye
// Remove someVariable from the example shared object.
in
ix
/l
delete example.data.someVariable;
et
.n
clear( )方法删除整个共享对象,实际上就是删除硬盘中的.sol文件,看下面的代码:
dn
cs
// Create a shared object and store some data in it
/b !
g.
:/ 译
lo
var example:SharedObject = SharedObject.getLocal( "example" );
tp 翻
ht 青
example.data.someData = "a value";
常
// Displays: a value
trace( example.data.someData );
// Remove the shared object from the disk
example.clear( );
// Displays: undefined
trace( example.data.someData );
需要注意的地方是清除数据后,共享对象的引用仍然是有效的,这是还是可以重新添加数据进
行保存。
347. 17.6. 序列化自定义类
问题
我想把自定义类实例存储到LSO
解决办法
使用flash.net.registerClassAlias( )方法保留类型信息并把类实例添加到共享对象的data属性上。
讨论
LSOs 使用特殊的二进制格式,Action Message Format (AMF),当要在LSO中存储类实例时,实
例会被编码为包含属性的普通的object。这样当重新从共享对象中读取实例时,已经不是原来的
类实例了,因为已不能根据类型信息解码回来。
flash.net包中的 registerClassAlias( )方法就是为解决这个问题的,这个方法的使用是很简单的,
在AS1.0和AS2.0中写法是Object.registerClass( ),但是在AS3.0里已经被删除了,取而代之的是
flash.net.registerClassAlias( )。
23
01
registerClassAlias( )方法需要两个参数,第一个参数表示类的别名,可以用任意字符串表示别名 ,
ye
比如modal包中有个Person类,别名可以是modal.Person,第二个参数类引用。
in
ix
registerClassAlias( "somePackage.ExampleClass", ExampleClass );
/l
et
这个代码的作用是把这个类的信息存进 LSO,当读取数据时,Flash 播放器就知道这个 object
.n
到底是什么类。
dn
cs
下面的例子完整实现了类实例的保存,首先创建自定义类:
/b !
g.
:/ 译
lo
// Create a Person class in the model package
tp 翻
ht 青
package model {
常
public class Person {
private var _firstName:String;
private var _age:int;
public function Person(firstName:String, age:int) {
_firstName = firstName;
_age = age;
}
public function toString( ):String {
return _firstName + " is " + _age + " years old";
}
}
348. }
接着,编写主类读取和写入数据
package {
import flash.net.registerClassAlias;
import flash.net.SharedObject;
import model.Person;
public class Example {
public function Example( ) {
// Map "model.Person" to the Person class
registerClassAlias( "model.Person", Person );
// Create a shared object and store a Person instance in it
var example:SharedObject = SharedObject.getLocal( "example" );
23
// Test to see if the person instance has been saved already
01
ye
if ( example.data.person == undefined ) {
in
ix
trace( "first time, saving person instance" );
/l
et
var person:Person = new Person("Darron", 24);
.n
// Write the class instance to the local shared object
dn
cs
example.data.person = person;
/b !
g.
:/ 译
lo
} else {
tp 翻
ht 青
trace( "person instance already saved, using stored values" );
常
}
/* Every time this code is executed, the following is displayed:
Darron is 24 years old
*/
trace( example.data.person.toString( ) );
}
这里需要注意的是registerClassAlias( )必须在SharedObject.getLocal( )方法之前调用才有效。否则
的话共享对象会把Person解释为普通的object类型进行存储。
353. 1. 创建动态的LocalConnection子类,使用这个实例作为所有接收端本地连接
2. 创建LocalConnection子类重写发送数据的方法
3. 重定向客户对象的请求
下面我们看一下每一种解决办法
要想在运行时修改类的方法,该类必须申明为 dynamic。在ActionScript3.0中最简单的方法就是
继承LocalConnection类并申明为dynamic类,使用这个类作为接收端:
package {
import flash.net.LocalConnection;
// 创建动态的Location Connection 类
dynamic public class DynamicLocalConnection extends LocalConnection {
//
}
}
23
01
现在用这个DynamicLocalConnection代替LocalConnection类,在用以前的方法创建接收函数:
ye
// 在接收端Flash创建本地连接对象 in
ix
var receiver:DynamicLocalConnection = new DynamicLocalConnection( );
/l
et
// 通知本地连接实例监听来自"_exampleChannel"通道的信息
.n
dn
receiver.connect( "_exampleChannel" );
cs
/b !
g.
// 因为DynamicLocalConnection类是动态的,所以我们可以在实例上创建
:/ 译
lo
tp 翻
//新的函数来处理本地连接信息
ht 青
常
receiver.example = function( ):void {
trace( "communication received" );
};
第一种方法的优点是很灵活,你可以把这个类放到其他项目中继续适用,缺点也很明显,就是
太灵活了而无法为其定义接口,这不符合面向对象设计原则。
第二中方法也是继承LocalConnection类,并且创建指定的方法用于处理信息,例如下面的类定
义了一个example( )方法
package {
import flash.net.LocalConnection;
public class ExampleLocalConnection extends LocalConnection {
public function ExampleLocalConnection( ) {}
public function example( ):void {
trace("communication received");
354. }
}
}
第三种方法使用LocalConnection的属性client来重定向请求,client对象必须是public的方法,看
下面的例子:
package {
import flash.net.LocalConnection;
public class Example {
private var _localConnection:LocalConnection;
public function Example( ) {
_localConnection = new LocalConnection( );
_localConnection.connect( "_exampleChannel" );
23
_localConnection.client = this;
01
ye
}
public function example( ):void {
in
ix
/l
trace("communication received");
et
.n
}
dn
cs
}
/b !
g.
:/ 译
lo
}
tp 翻
我们已经设置好了接收的swf,下一步编写发送的swf文件,发送端使用 LocalConnection.send( )
ht 青
常
方法发送信息。send( )方法的第一个参数指定通道名称,第二个参数指定接收端被调用的方法
名称。当一个flash接收到信息时,他将会触发这个方法。
下面的例子触发名为example的远程方法:
//通过"_exampleChannel" 通道发送信息
//触发接收端的example( )方法
var sender:LocalConnection = new LocalConnection( );
sender.send( "_exampleChannel", "example" );
当使用 LocalConnection类时,通信是多对一的关系,也就是多个发送方,但接收方只有一个,
如果接收端试图连接另一个接收端已经打开的通道,则 connect( ) 方法将抛出异常,应该用
try...catch 来检测是否能成功连接:
var receiver:LocalConnection = new LocalConnection( );
try {
receiver.connect( "_exampleChannel" );
355. } catch( e:Error ) {
// Error 不能在_exampleChannel 通道上建立连接,因为这个连接已经被使用了
}
要让 LocalConnection 能正常工作,发送端和接收端必须在同一个电脑上却都在运行才可,如果
在同一台电脑不同的时间进行通信,则要使用 LSO。
18.2. 发送数据
问题
我想在几个flash之间传送数据
解决办法
23
通过send( )方法的第三个参数传递数据
01
ye
讨论
in
ix
send( )方法的第三个参数即传递的数据,前两个参数是必须的:通道名称,接收端方法名称。
/l
需要注意的是这个方法名称不能和 LocalConnection类内部的属性和方法名称冲突,否则 send( )
et
.n
将调用失败,send, connect, close, allowDomain, allowInsecureDomain, client, and domain.这些名称
dn
不能作为参数使用。
cs
/b !
g.
在接收端应创建一个接收方法来接收发送过来的数据,下面的例子中,接收端创建 example( ).
:/ 译
lo
作为接收函数:
tp 翻
ht 青
package {
常
import flash.net.LocalConnection;
public class ExampleReceiver {
private var _receiver:LocalConnection;
public function ExampleReceiver( ) {
// 实例化接收端本地连接,监听"_exampleChannel"通道
_receiver = new LocalConnection( );
_receiver.connect( "_exampleChannel" );
_receiver.client = this;
}
public function example( str:String, num:Number, bool:Boolean):void {
trace( "The parameters are: " + str + "n" + num + "n" + bool );
356. }
}
}
发送端的代码如下:
// 发送三个参数给接收端
var sender:LocalConnection = new LocalConnection( );
sender.send( "_exampleChannel", "example", "a string", 6.5, true);
发送数据的数据类型不限于基本数据类型,还可以是复合类型:Object, Array, Date, TextFormat,
和XML.
另外也可以是自定义数据类型,发送和读取自定义类对象的方式和LSO的方式差不多,更多细
节请看第17.6章, 基本步骤如下:
1. 定义自定义类,在发送和接收Flash中引入
2. 使用flash.net.registerClass( )注册自定义类对象,注意在所有的flash中要用相同的名称别名
23
3. 通过本地连接的Send a class instance over a local connection with the send( ) method.
01
ye
下面的例子演示如何传递自定义类对象,首先定义自定义类Person:
in
ix
package model {
/l
et
public class Person {
.n
dn
private var _firstName:String;
cs
private var _age:int;
/b !
g.
:/ 译
lo
public function Person(firstName:String, age:int) {
tp 翻
ht 青
_firstName = firstName;
常
_age = age;
}
public function toString( ):String {
return _firstName + " is " + _age + " years old";
}
}
}
接收端代码:
import flash.net.registerClassAlias;
import model.Person;
registerClassAlias( "model.Person", model.Person );
357. package {
import flash.net.LocalConnection;
public class ExampleReceiver {
private var _localConnection:LocalConnection;
public function ExampleReceiver( ) {
_localConnection = new LocalConnection( );
_localConnection.connect("_exampleChannel");
_localConnection.client = this;
}
public function example( person:Person ):void {
trace( person.toString( ) );
}
23
}
01
ye
}
in
ix
发送端代码:
/l
et
package {
.n
import flash.net.registerClassAlias;
dn
cs
import model.Person;
/b !
g.
:/ 译
lo
public class ExampleSender {
tp 翻
ht 青
public function ExampleSender( ) {
常
registerClassAlias( "model.Person", Person );
// Create a Person instance to send across
var person:Person = new Person("Darron", 24);
// Create the local connection and send a Person instance
// to the receiving movie.
var sender:LocalConnection = new LocalConnection( );
sender.send( "_exampleChannel", "example", person );
}
}
}
358. 18.3. 基于本地连接通信的有效性验证
问题
我想知道发送的数据是否被接收端顺利收到
解决办法
设置接收端发送确认信息给发送端
讨论
要确认信息是否被收到,需要在接收端返回给发送端一条确认信息,为了验证信息是否被收到,
可以在接收端发送一条确认消息给发送端。由于本地连接自身的限制,一个通道不能用于双向
通信,因此需要各自建立一条通道互不干扰,具体步骤如下:
1. 接收端和发送端的代码请看第18.1章.
2. 在接收端通过新的通道"_exampleChannelReceipt "发送确认信息,使用 this.send( )方法把信
息发送给原始发送端。
23
3. 在发送端调用connect( )方法建立新通道"_exampleChannelReceipt"用于接收信息。.
01
ye
4. 在发送端创建处理方法接受信息。
下面是发送端和接收端代码片断:
in
ix
/l
首先是接收端代码,使用DynamicLocalConnection类:
et
.n
// 创建接受代码监听"_exampleChannel" 通道
dn
cs
var receiver:DynamicLocalConnection = new DynamicLocalConnection( );
/b !
g.
:/ 译
lo
receiver.connect( "_exampleChannel" );
tp 翻
ht 青
receiver.example = function( ):void {
常
this.send( "_exampleChannelReceipt", "receipt" );
};
发送端代码:
var sender:DynamicLocalConnection = new DynamicLocalConnection( );
seder.send( "_exampleChannel", "example" );
// 监听"_exampleChannelReceipt" 通道
sender.connect( "_exampleChannelReceipt" );
// 定义receipt( )方法接收返回信息
sender.receipt = function( ):void {
output.text = "Receiver has delivered sent receipt";
};
362. 在ActionScript 3.0里发送和读取的数据都会被改变。LoadVars类已经被flash.net的URLLoader所代
替。URLLoader 支持新的字符集映射,他们是 URLRequest, URLVariables, 和 URLStream 。这
些类提供了比以前更强大更灵活的处理能力。
有一些是没有改变的,比如读取的数据依然遵循 Flash 播放器标准安全沙漏体系,也就是说读
取的数据文件或脚本必须和 flash 在同一个域内。
19.1. 从文本文件中读取数据
问题
我想读取外部的文本文件的内容到flash上。
解决办法
23
使用URLLoader.load( )方法和DataFormat.VARIABLES 读取URL-编码数据
01
ye
讨论
in
ix
读取URL-编码数据时应该使用URLLoader.load( )方法。
/l
et
load( )方法需要一个URLRequest实例作为参数,该参数指向文本文件的URL,这个URL即可以
.n
是相对路径也可以是绝对路径。另外 URLLoader 需要进行配置把文本数据解释成URL-编码变
dn
量。设置URLLoader的dataFormat属性为DataFormat.VARIABLES常量:
cs
/b !
g.
import flash.net.*;
:/ 译
lo
tp 翻
// 首先创建URLLoader对象
ht 青
常
var example:URLLoader = new URLLoader( );
// 进行设置
example.dataFormat = DataFormat.VARIABLES;
// 读取绝对路径的URL
example.load( new URLRequest( "http://www.darronschall.com/example.txt" ) );
// 读取相对路径的URL,文本文件和swf文件在同一个目录下
example.load( new URLRequest( "example.txt" ) );
假设文本文件的内容如下:
someText=testing&someNumber=123
一旦调用了load( )方法,Flash播放器就会试图读取URL的数据填充URLLoader的data属性。读取
完成后,Flash会试图解码这些内容并发出complete事件,指示数据已经读取完毕。这时候你可
以添加处理函数来处理了。
363. 如果读取失败,根据失败的原因 URLLoader会发出不同类型的异常事件,因此除了监听完成事
件,还要监听那些异常事件。
load( )调用引发的异常有:
httpStatus
当试图读取数据,Flash播放器检测出错误的HTTP请求时发出
ioError
当遇到致命错误导致下载终止时发出
securityError
当试图读取安全沙漏允许以外的域 数据时发出
下面的例子演示监听各种事件:
package {
23
import flash.events.*;
01
ye
import flash.net.*;
import flash.util.trace;
in
ix
/l
public class Example {
et
.n
public function Example( ) {
dn
cs
// Create the URLLoader instance to be able to load data
/b !
g.
:/ 译
lo
var loader:URLLoader = new URLLoader( );
tp 翻
ht 青
// Define the event handlers to listen for success and failure
常
loader.addEventListener( IOErrorEvent.IO_ERROR, handleIOError );
loader.addEventListener( HTTPStatusEvent.HTTP_STATUS, handleHttpStatus );
loader.addEventListener( SecurityErrorEvent.SECURITY_ERROR,handleSecurityError );
loader.addEventListener( Event.COMPLETE, handleComplete );
// Configure the loader to load URL-encoded variables
loader.dataFormat = DataFormat.VARIABLES;
// Attempt to load some data
loader.load( new URLRequest( "example.txt" ) );
}
function handleIOError( event:IOErrorEvent ):void {
trace( "Load failed: IO error: " + event.text );
}
364. function handleHttpStatus( event:HTTPStatusEvent ):void {
trace( "Load failed: HTTP Status = " + event.status );
}
function handleSecurityError( event:SecurityErrorEvent ):void {
trace( "Load failed: Security Error: " + event.text );
}
function handleComplete( event:Event ):Void {
trace( "The data has successfully loaded" );
}
}
如果读取成功,Flash播放器把数据存在URLLoader实例的data属性上并发出complete事件。下面
的例子演示处理complete事件:
23
function handleComplete( event:Event ):void {
01
// 把event target 转换为URLLoader
ye
var loader:URLLoader = URLLoader( event.target );
in
ix
/l
// 输出获得的数据
et
.n
trace( "someText = " + data.someText );
dn
cs
trace( "someNumber = " + data.someNumber );
/b !
g.
:/ 译
lo
}
tp 翻
假设文本文件的内容如下:
ht 青
常
someText=ActionScript+3.0+Cookbook&someNumber=3
通过 for . . . in 语句遍历所有读取的变量:
function handleComplete( event:Event ):void {
var loader:URLLoader = URLLoader( event.target );
// Use a for . . . in loop to loop over all of the variables that
// were loaded
for ( var property
property:String in loader.data ) {
// property 就是data里的变量名
trace( property + " = " + loader.data[property] );
}
}
365. 19.2. 从服务端脚本中读取变量
问题
我想从服务端脚本(ColdFusion, Perl, PHP, etc.)中读取变量.
解决办法
使用URLLoader.load( )方法和DataFormat.VARIABLES 读取由服务端脚本产生的URL-编码数据
讨论
ActionScript读取服务端脚本数据和读取文本文件的操作是一样的,当这些数据是从服务端数据
库或其他资源中产生时,脚本必须输出为URL-编码的数据才行,如果你采用perl脚本,输出为
URL-编码,就不需要其他任何调整了,看下面的例子:
#!/usr/bin/perl
# In a more practical example this value would be retrieved
23
# from a database or other server-side resource.
01
ye
$someText = "test";
# 定义Content-Type 头.
in
ix
/l
print "Content-Type: text/plainnn";
et
.n
# Output the name-value pair.
dn
cs
print "someText=$someText";
/b !
g.
:/ 译
lo
ColdFusion:
tp 翻
ht 青
<cfsetting enablecfoutputonly="yes">
常
<cfprocessingdirective suppresswhitespace="yes">
<cfset someText = "test">
<cfoutput>someText=#someText#</cfoutput>
</cfprocessingdirective>
PHP:
<?php
$someText = "test";
echo "someText=$someText";
?>
上面的每个脚本例子中都返回一个变量:someText,用下面的ActionScript代码读取这个变量:
package {
import flash.events.*;
366. import flash.net.*;
import flash.util.trace;
public class Example( ) {
public function Example( ) {
// 创建URLLoader实例
var loader:URLLoader = new URLLoader( );
// 定义事件处理函数
loader.addEventListener( Event.COMPLETE, handleComplete );
// 设置读取的是 URL-编码 变量
loader.dataFormat = DataFormat.VARIABLES;
// 尝试读取数据
loader.load( new URLRequest( "getSomeText.cfm" ) );
23
}
01
ye
private function handleComplete( event:Event ):void {
in
ix
var loader:URLLoader = URLLoader( event.target );
/l
et
trace( "someText = " + loader.data.someText );
.n
}
dn
cs
}
/b !
g.
:/ 译
lo
}
tp 翻
ht 青
如果你事先不知道变量名,可以使用 for...in 语句遍历 data。
常
367. 19.3. 读取文本块(包括HTML XML)
( HTML
HTML和XML)
问题
我想读取文本块,如HTML或XML.
解决办法
使用URLLoader.load( )和DataFormat.TEXT
讨论
ActionScript 3.0 处理数据的方式已经和ActionScript 1.0 和2.0完全不同了,在以前的版本中,
LoadVars 实例有两个不同的回调函数用于处理通过URL读取的数据。当处理读取的数据时触发
onLoad( )回调函数,当数据读取完成时触发onData( )回调函数。
flash.net.URLLoader类和LoadVars类则不同,它只有一个complete事件和dataFormat属性用于决定
如何解释这些下载的数据。dataFormat属性如果为DataFormat.TEXT,则会把数据解释为普通的
文本内容,如果为DataFormat.VARIABLES则解释为URL-编码的变量,默认情况下,URLLoader
23
把数据解释为文本。
01
ye
假设从外部读取一个名为example.html的HTML数据,内容大致如下:
in
ix
<b>Title:</b> ActionScript 3.0 Cookbook<br />
/l
et
<b>Authors:</b> Joey Lott, Darron Schall, Keith Peters<br />
.n
dn
<b>Publisher URL:</b> <a href="http://www.oreilly.com">www.oreilly.com</a>
cs
下面的例子将读取并显示这些内容:
/b !
g.
:/ 译
lo
package {
tp 翻
ht 青
import flash.display.*;
常
import flash.text.*;
import flash.events.*
import flash.net.*;
public class HTMLLoadingExample extends Sprite {
private var _output:TextField;
public function HTMLLoadingExample( ) {
initializeOutput( );
loadData( );
}
private function initializeOutput( ):void {
_output = new TextField( );
368. _output.width = stage.stageWidth;
_output.height = stage.stageHeight;
_output.html = true; // Enable HTML for the text field
addChild( _output );
}
private function loadData( ):void {
var loader:URLLoader = new URLLoader( );
// Instruct the loader to read the file as plain text - This line is not
// necessary because the dataFormat is DataFormat.TEXT by default.
loader.dataFormat = DataFormat.TEXT;
// Register an event handler for when the data is finished downloading
loader.addEventListener( Event.COMPLETE, handleComplete );
23
// Load the HTML text from the example.html file
01
ye
loader.load( new URLRequest( "example.html" ) );
in
ix
}
/l
et
private function handleComplete( event:Event ):void {
.n
var loader:URLLoader = URLLoader( event.target );
dn
cs
// Assign the htmlText of the text field to the HTML text that was contained
/b !
g.
:/ 译
lo
// in example.html. The data property of the URLLoader is the file contents.
tp 翻
ht 青
_output.htmlText = loader.data;
常
}
}
}
data 属性的数据类型是根据 dataFormat 属性的设置而决定的,如果设置为 DataFormat.TEXT,
则 data 属性的值为 String 类型,如果设置 DataFormat.VARIABLES,则是 Object 类型,如果设
置为 DataFormat.BINARY,则 data 的数据类型为 flash.util.ByteArray。
370. loader.load( new URLRequest( "example.txt" ) );
}
private function handleProgress( event:ProgressEvent ):void {
// Calculate the percentage by multiplying the loaded-to-total
// ratio by 100
var percent:Number = Math.round( event.bytesLoaded / event.bytesTotal * 100 );
_output.text = " Loaded: " + event.bytesLoaded + "n"
+ " Total: " + event.bytesTotal + "n"
+ "Percent: " + percent;
}
}
}
23
需要注意的是 URLLoader 不能边下载边处理数据,如果想这样可以用 URLStream,这将在下一
01
节讨论。
ye
in
ix
/l
et
.n
dn
cs
19.5. 边下载边访问数据
/b !
g.
:/ 译
lo
tp 翻
问题
ht 青
常
我想边下载边访问数据
解决办法
使用 flash.net.URLStream 实例可在下在过程中立即读取二进制数据
讨论
第19.4章 讨论了如何检测数据下载进度,但是由于URLLoader类本身的限制,只能等数据全部
下载完才能读取数据,要想边下载边读取可使用URLStream类代替之。
URLStream 可 以 边 下 载 边 以 二 进 制 形 式 读 取 数 据 , URLLoader 的 dataFormat 属 性 为
DataFormat.BINARY时和URLStream非常类似,它们有相同的事件,关键的区别是处理progress
事件的方式不同而已。
在 URLLoader 中, progress 事件在检测多少已下载的数据或显示下载进度方面有用,而
URLStream 中的 progress 事件允许使用 bytesAvailable 属性和下列方法如 readInt( ), readByte( ),
readBoolean( )检查下载的数据。
当使用 URLLoader 不是很有效时或访问的数据都是二进制时最好使用 URLStream。
371. 下面的例子代码使用 URLStream 读取一个.txt 文件,访问文件中的字节数据:
package {
import flash.display.*;
import flash.events.*
import flash.net.*;
public class CheckProgressExample extends Sprite {
public function CheckProgressExample( ) {
var streamer:URLStream = new URLStream( );
// Listen for the progress event to act on data
// as it is received
streamer.addEventListener( ProgressEvent.PROGRESS, handleProgress );
streamer.load( new URLRequest( "example.txt" ) );
23
}
01
ye
private function handleProgress( event:ProgressEvent ):void {
in
ix
// Cast the target of the vent as a URLStream
/l
et
var streamer:URLStream = URLStream( event.target );
.n
// 遍历所有已读取得字节数据
dn
cs
while ( streamer.bytesAvailable > 0 ) {
/b !
g.
:/ 译
lo
// Read a byte value and output it to the console window
tp 翻
ht 青
trace( "Read byte: " + streamer.readByte( ) );
常
}
}
}
}
从上面的代码中可以看到,在进行读之前最好检测下 bytesAvailable 属性,如果读取的字节超出
缓冲的结果则会抛出 EOFError 异常。
372. 19.6. 发送数据给服务端脚本
问题
我想发送数据给服务端脚本
解决办法
创建一个包含数据的URLRequest实 例,并用 flash.net.sendToURL( )方法传递给服务端脚本,如果想
在新的浏览器窗口中打开可使用flash.net.navigateToURL( )方法,如果想知道执行结果,可使用
URLLoader.load( )方法。
讨论
如果不用处理结果,最好是用flash.net.sendToUrl( )方法发送数据,例如发送一个web表 单 数 据 ,
而服务端脚本处理后不用返回结果显示出来。 但是sendToURL( )方法也就不会返回数据是否收到
的确认消息了,因此在实际应用中并不很实用。如果只是想显示一个信息如 "Thank you for
submitting the form,"确认信息,表示服务端已成功收到, 可使用URLLoader.load( )方 法 ,3.12节 和
23
19.7节.都讨论过这个方法。
01
ye
通过sendToUrl传递给服务端的URLRequest实例包含传递的数据以及传输方式,如果data属性设
置为URLVariables实例,则发送名称/值 对到服务器,也可以是flash.util.ByteArray,通过 HTTP in
ix
POST 方 式 传 递 二 进 制 数 据 到 服 务 器 , 或 者 是 String 类 型 的 数 据 作 为 XML-RPC ( 看
/l
et
http://www.xmlrpc.com)请求发送到服务器。下面的代码演示:
.n
dn
function sendData( ):void {
cs
/b !
// Create a request that sends data to the process.cfm page
g.
:/ 译
lo
var request:URLRequest = new URLRequest( "process.cfm" );
tp 翻
ht 青
// Create some variables to send, someText and someNumber.
常
var variables:URLVariables = new URLVariables( );
variables.someText = "Some text to send";
variables.someNumber = 26.2;
// Set the data to be sent to the variables, created earlier
request.data = variables;
// Send the data to the script for processing
sendToURL( request );
}
传递给URLRequest的URL既可以是绝对地址也可以是相对地址,这可以通过Flash播放器安全沙
漏进行管理,有关安全方面看第3.12节。
// 设置url属性为绝对地址
373. request.url = "http://www.darronschall.com/cgi-bin/submitVars.cgi";
// 设置为相对于swf 文件的地址
request.url = "cgi-bin/submitVars.cgi";
通过sendToURL( )方法发送的数据被服务器处理后,服务器返回的信息会被Flash播放器忽略掉,
要想发送数据且打开指定的浏览器可使用 navigateToURL( )方法,这个方法也 sendToURL( )方法
几乎一样, 只是多了个参数, 如果设为_blank即会在新的窗口显示响应结果,还可设置为_self 或
_parent 。
// 在新的窗口中发送数据
navigateToURL( request, "_blank" );
如果想接收服务器返回的结果,可使用URLLoader.load( )方法(看3.12节 和19.7节).
默认情况下, 使用sendToURL( ) 或navigateToURL( )方法,数据都是通过HTTP POST方法传输的 ,
URLRequest的method属性指定数据传输的方式,如URLRequestMethod.GET指 HTTP GET 和
URLRequestMethod.POST指HTTP POST。
23
var request:URLRequest = new URLRequest( "cgi-bin/submit.cgi" );
01
// 创建发送的变量
ye
var variables:URLVariables = new URLVariables( );
in
ix
/l
variables.someText = "Post me!";
et
.n
request.data = variables;
dn
cs
// 设置传输方式HTTP POST
/b !
g.
:/ 译
lo
request.method = URLRequestMethod.POST;
tp 翻
ht 青
// Send the request and open the response in a new window
常
navigateToURL( request, "_blank" );
374. 19.7. 发送变量并处理返回结果
问题
我想发送变量到服务器脚本,并处理返回的结果
解决办法
使用URLLoader.load( )方法且设置URLRequest实例的data属性
讨论
当需要处理服务器返回的结果时,应该使用URLLoader.load( )方法。比如一个flash的 商店 程序 ,
它的商品分类都存在服务器数据库里,当用户点击一个分类, flash发送这个分类id给服务器脚
本并返回这个分类的商品数据。
URLLoader.load( ) 发送数据给服务器脚本的方式和sendToURL( ) ,navigateToURL( ) 是一样的,
下面的完整例子发送数据到服务器脚本并返回URL-编码的值:
package {
23
01
import flash.display.*;
ye
import flash.text.*; in
ix
/l
import flash.events.*
et
.n
import flash.net.*;
dn
public class SendAndLoadExample extends Sprite {
cs
/b !
g.
private var _output:TextField;
:/ 译
lo
tp 翻
public function SendAndLoadExample( ) {
ht 青
常
initializeOutput( );
sendData( );
}
private function initializeOutput( ):void {
_output = new TextField();
_output.width = stage.stageWidth;
_output.height = stage.stageHeight;
addChild( output );
}
private function sendData( ):Void {
// Create a URLRequest to contain the data to send
// to process.cfm
375. var request:URLRequest = new URLRequest( "process.cfm" );
// Create name-value pairs to send to the server
var variables:URLVariables = new URLVariables( );
variables.method = "getProductDetail"
variables.productId = 2;
request.data = variables;
// Create a URLLoader to send the data and receive a
// response
var loader:URLLoader = new URLLoader( );
// Expect the script to return URL-encoded variables
loader.dataFormat = DataFormat.VARIABLES;
// Listen for the complete event to read the server response
23
loader.addEventListener( Event.COMPLETE, handleComplete );
01
ye
// Send the data in the URLRequest off to the script
in
ix
loader.load( request );
/l
et
}
.n
private function handleComplete( event:Event ):void {
dn
cs
var loader:URLLoader = URLLoader( event.target );
/b !
g.
:/ 译
lo
// Expect the script to return name and description variables.
tp 翻
ht 青
// Display these values in a text field on the screen.
常
_output.text = "Name: " + loader.data.name + "n"
+ "Description: " + loader.data.description;
}
}
}
376. 20.0. 简介
XML 是一种结构化的描述数据形式,因其简单,灵活,尤其是在数据交换和可移植等优点现
已成为事实上的工业标准。
我们在使用ActionScript过程中,XML是经常碰到的,第19章 介绍了如何发送和读取URL-编码
的数据格式。这种格式传递简单数据还可以但是如果是复杂的数据或Unicode字符串, XML因其
结构化优点就表现出来了。 例如,如果从一个文本文件中读取数据转换为基本类型如string,URL-
编码数据,如下面的那样,使用URLLoader对象读取:
myString=a+string+value
但是当你从外部数据源读取的数据并不是都能表现为URL-编码格式的字符串。例如向下面的那
样,没对数据是用*分开的,而每对键值对都是用|分开的:
myObject=prop0|val0*prop1|val1*prop2|val2
当读取到这些数据后,还将使用String.split( ) 来分离这些元素,虽然这样做也能达到目的,但
是如果使用XML就简单多了。例如,同样的数据用XML表示是这样的:
23
01
<myObject>
ye
<prop0>val0</prop0> in
ix
<prop1>val1</prop1>
/l
et
<prop2>val2</prop2>
.n
dn
</myObject>
cs
/b !
g.
XML格式体现出比URL-编码格式更多的优点,如:
:/ 译
lo
1。当手动创建XML或程序创建的(ColdFusion,PHP等)很容易表现复杂数据。
tp 翻
ht 青
2。大多数服务端脚本都提供了内建的支持读写XML数据的功能。
常
3。XML已经是所有程序和平台传输和存储数据的通用标准。
当然,在Flash播放器中传输数据的方式并不局限于XML一种,我们将在第19章,21章,和24
章讨论其他通讯方式。这一章将重点讨论XML,一个交换数据的工业标准,而且使用它并不需
要额外的服务端软件(Flash Remoting和Sockets) XML已经成为ActionScript重要组成部分。
。
ActionScript 3.0 新增了新的操作XML的语法,即 ECMAScript for XML,也叫E4X,提供一种比
Document Object Model (DOM)更简单更容易访问XML的新方式。使用E4X,你会发现操作XML
比以前更简单了,另外如果你是头一次操作XML,那么E4X也是很容易学习的。
这一章我们将涉及下列一些技术名词:
XML document
包含XML的文件,也指读取和发送XML的数据,XML文档的概念不要和XMLDocument类搞混。
377. XML包
一个XML包指从整个XML文档中取出的片断
XML 节点e
XML最基本的块,节点可以是元素,文本节点,属性等等
XML 元素
这个 术 语 和 "Tag"意义 类 似 , 更 确 切 地 说 , 元 素 包 含 tags 。元 素 必 须 有 开 始 和 结 束 标 签
(<element></element>)或(<element />).
Root 节点
XML层级元素中最顶层的元素
23
01
Text 节点
ye
包含文本的节点,一般都在元素里面
in
ix
/l
et
.n
Attribute(属性)
dn
cs
元素的一部分,如<element name="value">,name="value"就是属性.
/b !
g.
:/ 译
lo
tp 翻
XML 声明
ht 青
常
典型的申明如<?xml version="1.0" ?>.
XML 树
XML 数据的节点层级构成 XML 树
378. 20.1. 理解XML
XML XML
XML结构(读写XML
XML)
问题
我想知道如何读写XML
解决办法
XML是以层级和标签为基础的,如果你熟悉HTML,那学习XML应该会很容易
讨论
虽然读写XML并不是ActionScript所专有,不过懂得如何这项技能仍然是很有好处的,如果你还
不熟悉XML,没关系,然我们一步步学习它。
XML是结构化数据的表现形式,这意味着要显示定义数据内容,例如,没有XML,你的数据时
这样的:
Jerry,Carolyn,Laura
23
使用XML后:
01
ye
<family>
<father>Jerry</father>
in
ix
/l
<mother>Carolyn</mother>
et
.n
<sister>Laura</sister>
dn
cs
</family>
/b !
g.
:/ 译
lo
可以看出XML表现的数据内容更丰富,关于XML还有其他一些注意点:
tp 翻
XML 以大量的节点为基础,像上面的例子中<family>是一个节点,这些节点被称为元素,还
ht 青
有Jerry, Carolyn,和Laura 这些节点被称为 text
常
text节点。
每个 XML元素 都有一个开始和结束标签,如上面的 <family> 为开 始标签,结束标签为
</family>。
元素可嵌套节点(元素或text节点) ,如上面的例子中<family>元素是根节点,他包含<father>,
<mother>, 和<sister>元素,这些嵌套的节点被称为子节点,每个子节点又可以嵌套节点,不
过这几个子节点都是text节点。
还有另一种节点,我们称之为属性,属性是特殊的节点附属于一个元素。如果你熟悉HTML的
话,对属性的概念应该很熟悉了,比如<a>元素包含HRef属性,下面的例子用属性代替嵌套节
点的写法:
<family father="Jerry" mother="Carolyn" sister="Laura" />
你可能会问什么时候该使用属性而不是嵌套节点呢?为什么要这样呢?这个往往是个人喜好问
题,有时候使用属性使XML更加易懂些,一般如果值内容比较短时建议使用属性,如果数据比
较大最好还是用嵌套节点比较好。
379. 当然也可以两者一起使用,如下面的例子,<article> 元素包含title和author属性,而把嵌套的text
节点用于显示文章主体:
<article title="XML: It's Not Just for Geeks" author="Samuel R.
Shimowitz">
My friends couldn't believe it when I started working with XML.
I became an outcast, confined to my dark office illuminated only
by the glow of my trusty CRT.
</article>
20.2. 创建XML
XML
XML对象
23
01
ye
问题
in
ix
我想创建一个XML对象用于存储数据
/l
et
解决办法
.n
使用下列方式之一创建XML对象:
dn
cs
创建XML对象并直接用XML进行赋值;
/b !
g.
:/ 译
lo
传递XML字符串给XML构造函数
tp 翻
创建一个空的XML对象并使用E4X填充数据
ht 青
创建空的对象,从外部读取XML数据
常
讨论
在ActionScript很多地方都会用到XML对象,下面是最简单的方式创建XML对象:
var example:XML = <abc><a>eh</a><b>bee</b><c>see</c></abc>;
这是E4X表达式的例子,注意等号右边没有引号,ActionScript编译器知道右边的表达式就是
XML。
上面的XML表达式是静态的,现在看一下如何创建动态的XML,在XML表达式中可以用{和}
引入变量。例如要创建一个包含用户名和分数的XML发送到服务端,可以这样:
// 创建两个变量
var username:String = "Darron";
var score:int = 1000;
var example:XML = <gamescore>
380. <username>{username}</username>
<score>{score}</score>
</gamescore>;
也可以先创建字符串,在传入XML构造器:
var str:String = "<gamescore><username>" + username + "</username>"
+ "<score>" + score + "</score></gamescore>";
var example:XML = new XML( str );
20.3. 添加XML
XML
XML元素
问题
23
01
我想构造一个XML对象,然后往内添加元素
ye
解决办法 in
ix
/l
使用E4X语法创建子元素并添加到XML树中。另外用insertChildBefore( )和insertChildAfter( ) 方
et
法更容易控制元素的添加。
.n
dn
讨论
cs
/b !
g.
在日常工作中经常碰到往XML对象里添加新节点,然后把XML传递给其他应用程序。用E4X语
:/ 译
lo
法添加新节点是非常简单的,只要用操作符(.),跟一般的对象属性操作基本类似,看下面的
tp 翻
ht 青
例子:
常
// 创建一个XML实例
var example:XML = <example />;
// 创建新的节点
example.newElement = <newElement />;
/* 显示:
<example>
<newElement/>
</example>
*/
trace( example );
上面的代码中,通过newElement属性来添加新的元素,属性名和内容不一定要相同,如下面的
例子:
381. var example:XML = <example />;
example.emptyElement = "";
/* 显示:
<example>
<emptyElement/>
</example>
*/
trace( example );
除了用(。)运算符外还可以用([ 和 ]) 如 :
,
var example:XML = <example />;
var id:int = 10;
example[ "user" + id ] = "";
23
/* 显示:
01
ye
<example>
in
ix
<user10/>
/l
et
</example>
.n
*/
dn
cs
trace( example );
/b !
g.
:/ 译
lo
两种写法基本上通用,不过还是有区别的,如下面的例子编译器就会报错:
tp 翻
ht 青
example.some-element = ""; //编译错误
常
这种情况下只能用[]来设置属性名了:
example[ "some-element" ] = "";
还有个问题需要注意,这种方法增加的节点都是添加在XML树的尾部,要解决这个问题,可用
insertChildBefore( )和insertChildAfter( ) 方法来控制插入的位置。insertChildBefore( ) 方法在当前
元素位置前插入新元素,而insertChildAfter( ) 在当前元素的后面插入,看下面的例子:
var example:XML = <example/>;
example.two = "";
example = example.insertChildBefore( example.two, <one /> );
example = example.insertChildAfter( example.two, <three /> );
/* 显示:
<example>
<one/>
382. <two/>
<three/>
</example>
*/
trace( example );
要注意的是这两个方法不是修改原来的 XML,而是返回新的 XML 实例。
20.4. 添加文本节点
问题
23
我想添加文本节点
01
ye
解决办法
in
ix
即可使用E4X语法创建文本节点并插入到 XML树中,也可用 appendChild( ), prependChild( ),
/l
insertChildAfter( ), 和insertChildBefore( ) 方法进行更多控制,灵活插入。
et
.n
讨论
dn
cs
插入文本节点的方法和第20.3章讲的插入元素是一样的,都是用(。)操作符,例如:
/b !
g.
:/ 译
lo
// 创建XML实例
tp 翻
ht 青
var example:XML = <example/>;
常
example.firstname = "Darron";
example.number = 24.9;
example.boolean = true;
example.abc = ["a", undefined, "b", "c", null, 7, false];
/* 显示:
<example>
<firstname>Darron</firstname>
<number>24.9</number>
<boolean>true</boolean>
<abc>a,,b,c,,7,false</abc>
</example>
383. */
trace( example );
这个例子同时添加了文本和元素节点,=号右边的值即作为左右元素节点的子文本节点。
要 向 进 行 更 多 的 控 制 可 是 使 用 appendChild( ), prependChild( ), insertChildBefore( ), or
insertChildAfter( )。
var example:XML = <example/>;
// 添加名为 two 元素以及值为2的文本子节点
example.appendChild( <two>2</two> );
// 在当前节点前面插入one 元素
example.prependChild( <one>"number 1"</one> );
// 在当前节点后面插入
example.insertChildAfter( example.one[0], 1.5 );
23
//在指定节点前插入
01
ye
example.insertChildBefore( example.two[0], <part>1.75</part> );
/* 显示:
in
ix
/l
<example>
et
.n
<one>"number 1"</one>
dn
cs
1.5
/b !
g.
:/ 译
lo
<part>1.75</part>
tp 翻
ht 青
<two>2</two>
常
</example>
*/
trace( example );
上面的例子代码既加入了元素节点(<one>, <part>, <two>) 也加入了文本节点(1.5)。
384. 20.5. 在XML
XML
XML元素中添加属性
问题
我想为XML元素增加属性
解决办法
使用E4X的 @ 操作符
讨论
使用E4X的 @ 操作符可为元素添加新的属性,如:
elementNode.@attributeName = "value";
在元素节点后面使用(.)操作符,再跟上@ 操作符,指定属性名称,=号右边即是属性值:
var example:XML = <example><someElement/></example>;
// 添加属性
23
01
example.someElement.@number = 12.1;
ye
example.someElement.@string = "example"; in
ix
example.someElement.@boolean = true;
/l
et
example.someElement.@array = ["a", null, 7, undefined, "c"];
.n
dn
/* 显示:
cs
/b !
g.
<example>
:/ 译
lo
tp 翻
<someElement number="12.1" string="example" boolean="true"
ht 青
常
array="a,,7,,c"/>
</example>
*/
trace( example );
当使用这种语法时,属性名必须是合法的变量名称,也就是说必须是数字,字幕和下划线组成
且不能以数字开头。但是有时如果属性名包含一些特殊符号,则不能用 @操作符,必须加上[]
操作符,例如:
example.someElement.@["bad-variable-name"] = "yes";
在[]里还可用表达式产生动态属性名,这是种很实用的技巧:
example.someElement.@["color" + num] = "red";
385. 20.6. 读取XML
XML
XML树中的元素
问题
我想读取XML对象中的子元素
解决办法
使用 elements( ) 方法返回 XMLList 类型的所有元素,并用 for each 循环遍历
讨论
E4X提供了一个很方便的 elements( ) 方法,该方法返回所有XML对象的子元素节点,再通过
for each 循环即可访问整个XML树结构:
var menu:XML = <menu>
<menuitem label="File">
<menuitem label="New"/>
23
</menuitem>
01
ye
<menuitem label="Help">
in
ix
<menuitem label="About"/>
/l
et
</menuitem>
.n
dn
This is a text node
cs
</menu>;
/b !
g.
:/ 译
lo
for each ( var element:XML in menu.elements( ) ) {
tp 翻
ht 青
/* 显示:
常
File
Help
*/
trace( element.@label );
}
从上面的例子中我们看到elements( ) 方法只返回下一级的子元素节点,这里面不包括文本节点
和下一级节点,要向访问整个XML结构,还需进行递归循环处理:
var menu:XML = <menu>
<menuitem label="File">
<menuitem label="New"/>
</menuitem>
<menuitem label="Help">
386. <menuitem label="About"/>
</menuitem>
This is a text node
</menu>;
/* 显示:
File
New
Help
About
*/
walk( menu );
23
01
ye
// A recursive function that reaches every element in an XML tree
in
ix
function walk( node:XML ):void {
/l
et
// Loop over all of the child elements of the node
.n
for each ( var element:XML in node.elements( ) ) {
dn
cs
// Output the label attribute
/b !
g.
:/ 译
lo
trace( element.@label );
tp 翻
ht 青
// Recursively walk the child element to reach its children
常
walk( element );
}
}
387. 20.7. 通过名字查找元素节点
问题
我想通过节点名字来查找元素
解决办法
直接使用 E4X 的 . 加上属性名语法来查找元素
讨论
E4X 操作XML对象是非常简单的,比如每个元素节点,可直接访问元素名:
var fruit:XML = <fruit><name>Apple</name></fruit>;
// 显示: Apple
trace( fruit.name );
看,就是这么简单,用点操作符(.)即可,再看一下更复杂点的例子:
23
01
var author:XML = <author><name><firstName>Darron</firstName></name></author>;
ye
// 显示: Darron in
ix
trace( author.name.firstName );
/l
et
还有种简便的方法,即使用双点操作符(..)来跳过一级访问,如:
.n
dn
var author:XML = <author><name><firstName>Darron</firstName></name></author>;
cs
/b !
g.
// 显示: Darron
:/ 译
lo
tp 翻
trace( author..firstName );
ht 青
当有多个元素节点具有相同名称时,可通过索引值访问,这有点像数组,使用中括号,根据索
常
引值访问指定的元素节点,例如:
var items:XML = <items>
<item>
<name>Apple</name>
<color>red</color>
</item>
<item>
<name>Orange</name>
<color>orange</color>
</item>
</items>;
388. // 显示: Apple
trace( items.item[0].name );
// 显示: Orange
trace( items.item[1].name );
items.item 返回两个元素的 XMLList 对象,第一个元素的索引值为0, 二 个 为1, length( ) 方
第 用
法可得到找到的元素节点个数:
// 显示: 2
trace( items.item.length( ) );
如果想访问特定名称的元素节点,但又不知道有多少个,可用 for each 循环遍历:
var items:XML = <items>
<item>
<name>Apple</name>
23
<color>red</color>
01
ye
</item>
<item>
in
ix
/l
<name>Orange</name>
et
.n
<color>orange</color>
dn
cs
</item>
/b !
g.
:/ 译
lo
</items>;
tp 翻
// 使用双点操作符
ht 青
常
for each ( var name:XML in items..name ) {
/* 显示:
Apple
Orange
*/
trace( name );
}
也可用[]操作符来访问:
var nodeName:String = "color";
var fruit:XML = <fruit><color>red</color></fruit>;
// Displays: red
trace( fruit[nodeName] );
389. 需要注意的是[]操作符不能和双点操作符一起用
trace( fruit..[nodeName] ); // 导致编译错误
20.8. 读取文本节点
问题
我想解析出文本节点及其值
解决办法
使用E4X语法,或者用text( )方法返回元素的文本节点的 XMLList对象,再用toString( )方法把文
本节点转换为字符串,也可通过int( ) 或 Number( ) 将其转换为其他类型。
23
讨论
01
ye
第20.4节 讨论了如何创建文本节点,这一节将讨论如何读取文本节点的内容。比如下面的XML
包:
in
ix
/l
<book>
et
.n
<title>ActionScript 3.0 Cookbook</title>
dn
cs
</book>
/b !
g.
:/ 译
lo
根节点为<book>,其包含子元素节点<title>,<title> 元素又包含文本节点,其值为 ActionScript
tp 翻
3.0 Cookbook。
ht 青
常
可通过点操作符,根据节点名称找到元素,使用toString( )方法得到文本节点的值:
var book:XML = <book>
<title>ActionScript 3.0 Cookbook</title>
</book>;
// 用E4X 语法访问title元素
var title:String = book.title.toString( );
// 显示: ActionScript 3.0 Cookbook
trace( title );
另外可通过Boolean( ),int( )等函数转换文本节点数据类型:
var example:XML = <example>
<bool>true</bool>
<integer>12</integer>
390. <number>.9</number>
</example>;
// Convert a text node of "true" to boolean true
var bool:Boolean = Boolean( example.bool );
// Convert a text node of "12" to an integer
var integer:int = int( example.integer );
// Convert a text node of ".9" to a number
var number:Number = example.number;
/* 显示:
true
12
.9
23
*/
01
ye
trace( bool );
in
ix
trace( integer );
/l
et
trace( number );
.n
上面的代码有个小小的问题,即 Boolean( ) 转换可能会不成功,如果<bool> 元素的值如果是
dn
cs
TRue则可能得不到正确的值,最好是进行一下大小写转换,如:
/b !
g.
:/ 译
lo
var bool:Boolean = example.bool.toLowerCase( ) == "true";
tp 翻
再看一下面的例子,这个XML的根节点即包含元素节点也包含文本节点,如果想显示<fruit>的
ht 青
常
文本节点内容该怎么写呢?
var fruit:XML = <fruit>
<name>Apple</name>
An apple a day...
</fruit>;
var value:String = fruit.toString( );
/* 显示:
<fruit>
<name>Apple</name>
An apple a day...
</fruit>
*/
391. trace( value );
这种情况下,用text( ) 方法可正确返回文本节点内容了:
var fruit:XML = <fruit>
<name>Apple</name>
An apple a day...
</fruit>;
for each ( var textNode:XML in fruit.text( ) ) {
// 显示: An apple a day...
trace( textNode );
}
23
01
ye
20.9. 读取元素的属性 ix
in
/l
et
问题
.n
我想解析出元素的属性值
dn
cs
解决办法
/b !
g.
:/ 译
lo
使用 attributes( ) 方法返回指定元素的属性列表,或者通过名称用E4X的@操作符或attribute( )
tp 翻
ht 青
方法访问属性
常
讨论
通过 attributes( ) 方法返回当前元素节点所有属性的 XMLList 对象, XMLList对象是可索引的,
很像Array对象,可通过索引值访问属性值:
var fruit:XML = <fruit name="Apple" color="red" />;
var attributes:XMLList = fruit.attributes( );
// 显示: Apple
trace( attributes[0] );
// 显示: red
trace( attributes[1] );
上面的例子中只显示出属性值,用 name( ) 方法可显示出属性名,看下面的代码:
var fruit:XML = <fruit name="Apple" color="red" />;
// 显示: color
392. trace( fruit.attributes( )[1].name( ) );
通过for each 循环语句可遍历所有属性名和属性值,如:
var fruit:XML = <fruit name="Apple" color="red" />;
for each ( var attribute:XML in fruit.attributes( ) ) {
/* 显示:
name = Apple
color = red
*/
trace( attribute.name( ) + " = " + attribute.toString( ) );
}
下面是E4X的写法:
var fruit:XML = <fruit name="Apple" color="red" />;
23
// 显示: red
01
ye
trace( fruit.@color );
in
ix
还有种方法,可使用attribute( ) 方法并传入属性名作为参数,得到属性值:
/l
et
var fruit:XML = <fruit name="Apple" color="red" />;
.n
// 显示: red
dn
cs
trace( fruit.attribute("color") );
/b !
g.
:/ 译
lo
用(*)和@操作符可访问所有属性值,有些类似于attributes( )方法:
tp 翻
ht 青
var fruit:XML = <fruit name="Apple" color="red" />;
常
// 显示: Apple
trace( fruit.@*[0] );
// 显示: red
trace( fruit.@*[1] );
// 显示: 2
trace( fruit.@*.length( ) );
E4X 语法是很强大的,如果XML饱含有多个元素且有相同名称的属性,比如下面例子中的price
属性,看用E4X如何统计出price:
// Create a fictitious shopping cart
var cart:XML = <cart>
<item price=".98">crayons</item>
<item price="3.29">pencils</item>
393. <group>
<item price=".48">blue pen</item>
<item price=".48">black pen</item>
</group>
</cart>;
// Create a total variable to represent represent the cart total
var total:Number = 0;
// Find every price attribute, and add its value to the running total
for each ( var price:XML in cart..@price ) {
total += Number(price);
}
// 显示: 5.23
23
trace( total );
01
ye
如果属性名含有特殊字符,可用[]进行访问:
in
ix
var example:XML = <example bad-variable-name="yes" color12="blue" />;
/l
et
var num:Number = 12;
.n
// 显示: yes
dn
cs
trace( example.@["bad-variable-name"] );
/b !
g.
:/ 译
lo
// 显示: blue
tp 翻
ht 青
trace( example.@["color" + num] );
常
394. 20.10. 删除元素,文本节点和属性
问题
我想删除XML对象中的元素节点,文本节点或属性
解决办法
使用 delete 关键字
讨论
上面几节我们学习了如何添加元素,文本节点和属性到XML对象上。现在我们讨论如何删除这
些节点,秘密就在于delete 关键字,看例子:
var example:XML = <example>
<fruit color="Red">Apple</fruit>
<vegetable color="Green">Broccoli</vegetable>
23
<dairy color="White">Milk</dairy>
01
ye
</example>;
in
ix
// 删除fruit元素的color属性
/l
et
delete example.fruit.@color;
.n
// 删除dairy元素
dn
cs
delete example.dairy;
/b !
g.
:/ 译
lo
// 删除vegetable元素的文本节点
tp 翻
ht 青
delete example.vegetable.text( )[0];
常
/* 显示:
<example>
<fruit>Apple</fruit>
<vegetable color="Green"/>
</example>
*/
trace( example );
delete只能一次删除一个节点,如果要删除多个节点,可通过for循环进行删除:
var example:XML = <example>
<fruit color="red" name="Apple" />
</example>;
395. // Get an XMLList of the attributes for fruit
var attributes:XMLList = example.fruit.@*;
// Loop over the items backwards to delete every attribute.
// By removing items from the end of the array we avoid problems
// with the array indices changing while trying to loop over them.
for ( var i:int = attributes.length( ) - 1; i >= 0; i-- ) {
delete attributes[i];
}
/* 显示:
<example>
<fruit/>
</example>
23
*/
01
ye
trace( example );
in
ix
/l
et
.n
dn
cs
20.11. 载入XML
/b !
XML
g.
:/ 译
lo
tp 翻
ht 青
问题
常
我想从XML文档中或服务端脚本产生的XML中读取XML数据
解决办法
使用URLLoader.load( ) 方法且设置dataFormat属性为DataFormat.TEXT读取数据,通过complete
事件处理函数转换载入的数据为XML实例
讨论
ActionScript 3.0中发送和读取数据由新的URLLoader及其相关类完成,读取XML也没有什么特殊
的地方。
读取XML文件的步骤如下: 首先创建URLLoader实例以简单文本形式读取数据, 其dataFormat 属
性必须设置为DataFormat.Text,监听并添加complete事件处理函数,看下面的例子演示:
package {
import flash.display.*;
import flash.events.*;
396. import flash.net.*;
import flash.util.*;
public class LoadXMLExample extends Sprite {
public function LoadXMLExample( ) {
var loader:URLLoader = new URLLoader( );
loader.dataFormat = DataFormat.TEXT;
loader.addEventListener( Event.COMPLETE, handleComplete );
loader.load( new URLRequest( "example.xml" ) );
}
private function handleComplete( event:Event ):void {
try {
// Convert the downlaoded text into an XML instance
23
var example:XML = new XML( event.target.data );
01
ye
// At this point, example is ready to be used with E4X
in
ix
trace( example );
/l
et
} catch ( e:TypeError ) {
.n
// If we get here, that means the downloaded text could
dn
cs
// not be converted into an XML instance, probably because
/b !
g.
:/ 译
lo
// it is not formatted correctly.
tp 翻
ht 青
trace( "Could not parse text into XML" );
常
trace( e.message );
}
}
}
}
上面的例子中之所以用 try...catch 块,是考虑到读取的数据有可能不是 XML 格式数据,
TypeError 异常就是不能成功转换为 XML 实例时抛出的。
398. import flash.text.*;
import flash.filters.*;
import flash.events.*;
import flash.net.*;
public class XMLSendLoadExample extends Sprite {
private var _message:TextField;
private var _username:TextField;
private var _save:SimpleButton;
public function XMLSendLoadExample( ) {
initializeDispaly( );
}
private function initializeDispaly( ):void {
23
_message = new TextField( );
01
ye
_message.autoSize = TextFieldAutoSize.LEFT;
in
ix
_message.x = 10;
/l
et
_message.y = 10;
.n
_message.text = "Enter a user name";
dn
cs
_username = new TextField( );
/b !
g.
:/ 译
lo
_username.width = 100;
tp 翻
ht 青
_username.height = 18;
常
_username.x = 10;
_username.y = 30;
_username.type = TextFieldType.INPUT;
_username.border = true;
_username.background = true;
_save = new SimpleButton( );
_save.upState = createSaveButtonState( 0xFFCC33 );
_save.overState = createSaveButtonState( 0xFFFFFF );
_save.downState = createSaveButtonState( 0xCCCCCC );
_save.hitTestState = save.upState;
_save.x = 10;
399. _save.y = 50;
// When the save button is clicked, call the handleSave method
_save.addEventListener( MouseEvent.CLICK, handleSave );
addChild( _message );
addChild( _username );
addChild( _save );
}
// Creates a button state with a specific background color
private function createSaveButtonState( color:uint ):Sprite {
var state:Sprite = new Sprite( );
var label:TextField = new TextField( );
label.text = "Save";
23
label.x = 2;
01
ye
label.height = 18;
in
ix
label.width = 30;
/l
et
var background:Shape = new Shape( );
.n
background.graphics.beginFill( color );
dn
cs
background.graphics.lineStyle( 1, 0x000000 );
/b !
g.
:/ 译
lo
background.graphics.drawRoundRect( 0, 0, 32, 18, 9 );
tp 翻
ht 青
background.filters = [ new DropShadowFilter( 1 ) ];
常
state.addChild( background );
state.addChild( label );
return state;
}
private function handleSave( event:MouseEvent ):void {
// Generate a random score to save with the username
var score:int = Math.floor( Math.random( ) * 10 );
// Create a new XML instance containing the data to be saved
var dataToSave:XML = <gamescore>
<username>{username.text}</username>
<score>{score}</score>
400. </gamescore>;
// Point the request to the script that will handle the XML
var request:URLRequest = new URLRequest( "/gamescores.cfm" );
// Set the data property to the dataToSave XML instance to send the XML
// data to the server
request.data = dataToSave;
// Set the contentType to signal XML data being sent
request.contentType = "text/xml";
// Use the post method to send the data
request.method = URLRequestMethod.POST;
// Create a URLLoader to handle sending and loading of the XML data
var loader:URLLoader = new URLLoader( );
23
// When the server response is finished downloading, invoke handleResponse
01
ye
loader.addEventListener( Event.COMPLETE, handleResponse );
in
ix
// Finally, send off the XML data to the URL
/l
et
loader.load( request );
.n
}
dn
cs
private function handleResponse( event:Event ):void {
/b !
g.
:/ 译
lo
try {
tp 翻
ht 青
// Attempt to convert the server's response into XML
常
var success:XML = new XML( event.target.data );
// Inspect the value of the success element node
if ( success.toString( ) == "1" ) {
_message.text = "Saved successfully.";
} else {
_message.text = "Error encountered while saving.";
}
} catch ( e:TypeError ) {
// Display an error message since the server response was not understood
_message.text = "Could not parse XML response from server.";
}
401. }
}
}
URLRequest 对象的 contentType 属性默认设置为 application/x-www-form-urlencoded ,当发送
XML 数据时必须设置为 text/xml ,而且设置 method 属性为 URLRequestMethod.POST ,表示通
过 HTTP POST.发送数据。
下一步就是创建服务端脚本,首先看一下 Perl脚本,将下列代码保存为 gamescores.cgi (或
gamescores.pl):
#!/usr/bin/perl
# Flash/Perl+CGI XML interaction demo
# Arun Bhalla (arun@groogroo.com)
use strict;
use XML::Simple;
23
01
use CGI;
ye
my $ScoreFile = "scores.txt"; in
ix
# Here we assume that this CGI script is receiving XML in text/xml
/l
et
# form via POST. Because of this, the XML appears to the script
.n
dn
# via STDIN.
cs
/b !
g.
my $input = XMLin(join('',<STDIN>));
:/ 译
lo
tp 翻
# Write out the HTTP header
ht 青
常
print CGI::header('text/xml');
# Try to open score file for writing, or return an error message.
open(SCORES, ">> $ScoreFile") || (printMessage(0) &&
die "Error opening $ScoreFile");
# Save the score in a pipe-delimited text file.
print SCORES join('|', $input->{username}, $input->{score}), "n";
# Return the result in XML.
printMessage(1);
# Subroutine to output the result in XML.
sub printMessage {
my $value = shift;
my $message = {};
402. $message->{success} = $value;
print XMLout($message, keeproot => 1, rootname => 'success');
}
如果使用ColdFusion,看下面的例子代码,将它保存为gamescores.cfm :
<cfsilent>
<cfsetting enablecfoutputonly="Yes">
<cfset success = 0>
<cftry>
<!--- XML packet sent by Flash. --->
<cfset scores_xml = XmlParse( getHTTPRequestData( ).content ) >
<!--- Parse out the XML packet sent from Flash. --->
<!--- Grab the username and score from the XML document and save as
23
local variables so they are easier to work with. // --->
01
ye
<cfset username = scores_xml.gamescore.username.XmlText >
in
ix
<cfset score = scores_xml.gamescore.score.XmlText >
/l
et
<!--- Append the latest score to our scores file. This could also be
.n
stored in the database or another XML document. // --->
dn
cs
<cffile action="APPEND" file="#ExpandPath( 'scores.txt' )#"
/b !
g.
:/ 译
lo
output="#username#|#score#|#getHTTPRequestData( ).content#" addnewline="Yes">
tp 翻
ht 青
<cfset success = 1 >
常
<cfcatch type="Any">
<cfset success = 0 >
<cffile action="APPEND" file="#ExpandPath( 'attempts.txt' )#" output="ERROR"
addnewline="Yes">
</cfcatch>
</cftry>
</cfsilent>
<cfcontent type="text/xml">
<cfoutput><?xml version="1.0" ?><success>#success#</success></cfoutput>
<cfsetting showdebugoutput="no" enablecfoutputonly="No">
下面是PHP脚本,将它保存为gamescores.php:
403. <?php
// Read In XML from Raw Post Data.
$xml = $GLOBALS['HTTP_RAW_POST_DATA'];
// Process XML using DOM PHP extension.
$document = xmldoc($xml);
// Read root element <gameinfo>.
$rootElement = $document->root( );
// Read child nodes <username> and <score>.
$childNodes = $rootElement->children( );
$data = "";
// Loop through child nodes and place in array.
foreach($childNodes as $childNode){
23
// Add data to array;
01
ye
$name = $childNode->tagName( );
in
ix
$value = $childNode->get_content( );
/l
et
$data[$name] = $value;
.n
}
dn
cs
// Append data to scores.txt ( format: username|score )
/b !
g.
:/ 译
lo
$fp = fopen("scores.txt","a+");
tp 翻
ht 青
$dataString = $data['username'] . "|" . $data['score'] . "n";
常
fputs($fp,$dataString,strlen($dataString));
fclose($fp);
// Return success code to Flash
echo "<success>1</success>";
?>
404. 20.14. 搜索XML
XML
问题
我想根据某种规则搜索出XML对象的节点或属性
解决办法
使用 E4X 语法和XML对象的过滤器来筛选出特定的值
讨论
这一章讨论了如何用E4X 语法读写XML对象,通过E4X的XPath 来搜索XML文档,E4X称得上
是最简单的也是最强大的工具,如果你熟悉XPath的话,可用E4X的高级特性(如果过滤器)它
可根据布尔表达式筛选出指定元素节点。
现在我们开始练习,首先创建一个XML:
var foodgroup:XML = <foodgroup>
23
<fruits>
01
ye
<fruit color="red">Apple</fruit>
in
<fruit color="orange">Orange</fruit>
ix
/l
<fruit color="green">Pear</fruit>
et
.n
<fruit color="red">Watermelon</fruit>
dn
cs
<servings>3</servings>
/b !
g.
:/ 译
lo
</fruits>
tp 翻
ht 青
<vegetables>
常
<vegetable color="red">Tomato</vegetable>
<vegetable color="brown">Potato</vegetable>
<vegetable color="green">Broccoli</vegetable>
<servings>2</servings>
</vegetables>
</foodgroup>;
如果你事先知道元素节点名称,可直接通过点操作符定位,例如,要返回<fruit>元素节点集合,
可使用下面的表达式:
var fruitList:XMLList = foodgroup.fruits.fruit;
如果只关心某个<fruit> 元素节点,可根据索引值访问:
var theApple:XML = foodgroup.fruits.fruit[0];
如果你不知道某个节点的完整路径,可使用双点操作符来定位,例如,下面的表达式返回所有
405. <vegetable>节点:
var vegetableList:XMLList = foodgroup..vegetable;
(*) 即为“任意节点”,如下面的表达式返回所有<servings>元素节点:
var servings:XMLList = foodgroup.*.servings;
@ 符号用来访问属性,下面的例子返回<fruit> 节点下的color属性集合:
var colorValues:XMLList = foodgroup.fruits.fruit.@color;
好,现在我们看一下过滤器,使用(条件)语法来筛选特定的元素节点,条件表达式为布尔值,
过滤器作用在XML 或 XMLList 对象上。
例如,想筛选出<fruit>元素所有color属性为red的节点集合,可设置表达式为@color == "red"
/* 显示:
<fruit color="red">Apple</fruit>
<fruit color="red">Watermelon</fruit>
23
*/
01
ye
trace( foodgroup..fruit.( @color == "red" ) );
在这个例子中的表达式分为两部分:
in
ix
/l
foodgroups..fruit 部分返回所有<fruit> 元素节点。
et
过滤器应用在<fruit>节点的XMLList集合上并产生新的XMLList 对象。
.n
dn
上面的例子选出color属性为red的<fruit>元素节点,但是如果我想选出所有color属性为red的元素
cs
节点呢?这时可使用*号选出任何节点其color属性red的元素节点:
/b !
g.
:/ 译
lo
/* 显示:
tp 翻
ht 青
<fruit color="red">Apple</fruit>
常
<fruit color="red">Watermelon</fruit>
<vegetable color="red">Tomato</vegetable>
*/
trace( foodgroup..*.( hasOwnProperty( "@color" ) && @color == "red" ) );
上面的例子中,hasOwnProperty 检查元素是否有color属性,如果有在检查其值是否为 red 。.
过滤不仅仅用于筛选属性,还可筛选元素节点,例如下面的例子显示<fruit>元素节点的<name>
元素节点的文本值为Apple的color属性值:
var fruits:XML = <fruits>
<fruit color="red">
<name>Apple</name>
</fruit>
<fruit color="orange">
406. <name>Orange</name>
</fruit>
<fruit color="green">
<name>Pear</name>
</fruit>
<fruit color="red">
<name>Watermelon</name>
</fruit>
</fruits>;
// 显示: red
trace( fruits.fruit.(name == "Apple").@color );
可以看到过滤器的功能是如此强大,如果和正则表达式联合使用,那将会更强大,如下面的例
23
子使用正则表达式来找出<fruit>节点<name>节点的文本节点值中包含原因字母的集合:
01
ye
var fruits:XML = <fruits>
<fruit color="red">
in
ix
/l
<name>Apple</name>
et
.n
</fruit>
dn
cs
<fruit color="orange">
/b !
g.
:/ 译
lo
<name>Orange</name>
tp 翻
ht 青
</fruit>
常
<fruit color="green">
<name>Pear</name>
</fruit>
<fruit color="red">
<name>Watermelon</name>
</fruit>
</fruits>;
/* 显示:
<fruit color="red">
<name>Apple</name>
</fruit>
<fruit color="orange">
407. <name>Orange</name>
</fruit>
*/
trace( fruits.fruit.( /^[aeiouAEIOU].*/.test( name ) ) );
上面的代码创建了一个正则表达式(/之间的内容/) ,表示起始字母为元音字母,大小写无关,
后面跟随任意字母。test( ) 方法测试 name 元素节点的文本节点值是否符合要求。
20.15. 在XML
XML HTML
XML中使用HTML
HTML和特殊字符
问题
23
我想在XML使用HTML或其他一些特殊字符.
01
解决办法
ye
使用CDATA标签
in
ix
/l
讨论
et
.n
在XML中包含的特殊字符需用特殊方式进行处理,例如<和>在XML中作为分隔符,如果你直接
dn
在XML文档中的文本内容中使用它们,则会导致语法分析错误,例如:
cs
/b !
g.
<example>a < b</example>
:/ 译
lo
tp 翻
虽然 a < b 是作为文本节点内容,但是<符号在XML文档中是有特殊意义的,因此这将会导致
ht 青
解析错误,另一个普遍问题就是在XML中使用HTML,例如:
常
<htmlExample><a href="http://www.darronschall.com">Darron</a></htmlExample>
这里的HTML是作为XML而不是字符串,上面的XML以<htmlExample>为根节点,<a>为子节点
且其文本节点值为Darron。
在这些情况必须使用CDATA 标签,把括号中的数据只作为字符串显示。
CDATA标签以<![CDATA[ 开始,以 ]]> 结束.,上面的例子可改写成这样:
<example><![CDATA[a < b]]></example>
<htmlExample><![CDATA[<a href="http://www.darronschall.com">Darron</a>]]></htmlExample>
当 XML 被解析时,CDATA 标签中的内容原封不动。
427. 24.1. 连接Socket
Socket
Socket服务器
问题
我想连接socket服务器
解决办法
使用 Socket.connect( ) 或 XMLSocket.connect( ) 方法建立连接并监听connect事件确定连接是否建
立。
讨论
要连接 socket服务器,首先要知道域名或 IP地址,还要知道端口,不管是使用 Socket 还是
XMLSocket,连接步骤是一样的,都是用connect( )方法进行连接,该方法接受两个参数:
host
指定域名或IP地址,如www.example.com或192.168.1.101。
23
01
port
ye
数字,指定连接的端口号,必须大于1024,如果小于1024则需服务器提供策略文件允许。 in
ix
/l
因为是异步通信,connect( )方法不会等待结果而是继续执行下面的语句,因此需要注册事件监
et
听器来获取连接结果。
.n
dn
注册事件监听器必须在调用connect( )方法之前,当连接成功时connect事件就会触发,下面的例
cs
子演示连接本机2900端口:
/b !
g.
:/ 译
lo
package {
tp 翻
ht 青
import flash.display.Sprite;
常
import flash.events.*;
import flash.net.Socket;
public class SocketExample extends Sprite {
private var socket:Socket;
public function SocketExample( ) {
socket = new Socket( );
// Add an event listener to be notified when the connection
// is made
socket.addEventListener( Event.CONNECT, onConnect );
// Connect to the server
socket.connect( "localhost", 2900 );
428. }
private function onConnect( event:Event ):void {
trace( "The socket is now connected..." );
}
}
}
如果使用XMLSocket,代码也基本上相同,代码如下:
package {
import flash.display.Sprite;
import flash.events.*;
import flash.net.XMLSocket;
public class SocketExample extends Sprite {
23
private var socket:XMLSocket;
01
ye
public function SocketExample( ) {
in
ix
socket = new XMLSocket( );
/l
et
// Add an event listener to be notified when the connection is made
.n
socket.addEventListener( Event.CONNECT, onConnect );
dn
cs
// Connect to the server
/b !
g.
:/ 译
lo
socket.connect( "localhost", 2900 );
tp 翻
ht 青
}
常
private function onConnect( event:Event ):void {
trace( "The xml socket is now connected..." );
}
}
}
如果连接失败,可能的异常有:runtime error,ioError,securityError ,
记住,当用socket连接主机时,要遵循Flash Player安全沙漏规则:
swf 和主机必须在同一个域;
网络上的swf不能连接本地服务器;
本地的swf 不能访问任何网络资源;
要允许域名交叉访问或连接低于1024的端口,需要提供cross-domain 策略文件。
430. 利用Socket对象,完全可以用ActionScript写出一个Telnet和POP客户端,这两个协议都是以ASCII
字符为基础的,例如,连接一个POP服务器后,可用下面的代码执行USER命令:
// POP servers expect a newline (n) to execute the preceding command.
socket.writeUTFBytes("USER exampleUsernamen");
写入的数据实际上还没发送到服务器上,每个方法都死把数据累积到Socket对象上,例如下面的
四个代码并没有把数据发送出去:
socket.writeByte(1);
socket.writeByte(5);
socket.writeByte(4);
socket.writeByte(8);
当要发送数据时,必须调用flush( )方法,flush( )方法发送所有的数据并清空缓冲区:
socket.flush( );
XMLSocket类发送数据就比较简单了,发送数据的方法为send( ),send( )方法接受任意类型的数
23
01
据类型,它会把参数转换为字符串并发送给服务器,一般这个参数是一个XML对象:
ye
xmlSocket.send(xml); in
ix
实际上发送的数据类型是由服务器所决定的,如果服务器接受 XML 数据,那必须发送 XML 数
/l
et
据,如果服务器接受 URL-编码数据,则必须发送 URL-编码数据。
.n
dn
cs
/b !
g.
:/ 译
lo
tp 翻
24.3. 接收数据
ht 青
常
问题
我想接收socket服务器发送来的数据
解决办法
对于Socket实例可通过 ProgressEvent.SOCKET_DATA事件处理函数中读取数据,可用readByte( )
或readInt( )方法
对于XMLSocket实例可通data事件处理函数中读取XML数据
讨论
从socket中接收数据的方法取决于你使用socket类型,Socket和XMLSocket都可以接收数据,但是
两者实现方法有些不同,让我们先看看Socket类是如何做的。
正如前面所讲到的,Flash提供的socket通信方式是异步通信,也就是说仅仅创建socket连接并试图
读取数据这是不可能的,read方法读取数据时并不会等待数据传输过来而立即返回,如果数据还
没准备好而去读取数据会导致异常。
431. 当数据准备好了时,socketData事件就会触发,通过注册该事件处理函数,当数据发送过来时就
会触发,因此可通过该处理函数读取数据。
为了读取服务器发送来的数据,Socket 类提供了一系列 read 方法来读取不同类型的数据,例如
通过 readByte( )方法读取一个字节,readUnsignedInt( )方法读取一个无符号整数,具体看下面的
表格:
Table 24-1. Socket read methods for various datatypes
方法:返回类型 描述 字节数
readBoolean():Boolean 读取布尔型数据 1
readByte():int 读取一个字节数据 1
readDouble():Number 读取 IEEE 754 双精度浮点数 8
readFloat():Number 读取 IEEE 754 单精度浮点数 4
readInt():in 读取 32 位整数 4
readObject():* 读取 AMF 格式对象 N
readShort():int 读取 16 位整数 2
readUnsignedByte():uint 读取无符号字节 1
23
readUnsignedInt():uint 读取无符号 32 位整数 4
01
readUnsignedShort():uint 读取无符号 16 位整数 2
ye
readUTF():String 读取 UTF-8 字符串 in n
ix
/l
et
还有两个方法没有在上面的表格里,它们是readBytes( )和readUTFBytes( ),readBytes( )方法没有
.n
返回值,它接受三个参数:
dn
cs
bytes
/b !
g.
一个flash.util.ByteArray 实例填充读取的数据
:/ 译
lo
tp 翻
offset
ht 青
常
一个uint值指定读取数据的偏移量,默认为0
length
一个uint值表示读取的字节数,默认为0,表示所有的数据
readUTFBytes( )方法只接受一个参数,表示读取的 UTF-8 字节数,返回一个字符串。
下面的例子代码连接一个socket服务器并读取和显示服务器发送的数据:
package {
import flash.display.Sprite;
import flash.events.ProgressEvent;
import flash.net.Socket;
public class SocketExample extends Sprite {
private var socket:Socket;
public function SocketExample( ) {
432. socket = new Socket( );
// Listen for when data is received from the socket server
socket.addEventListener( ProgressEvent.SOCKET_DATA, onSocketData );
// Connect to the server
socket.connect( "localhost", 2900 );
}
private function onSocketData( event:ProgressEvent ):void {
trace( "Socket received " + socket.bytesAvailable + " byte(s) of data:" );
// Loop over all of the received data, and only read a byte if there
// is one available
while ( socket.bytesAvailable ) {
// Read a byte from the socket and display it
23
var data:int = socket.readByte( );
01
ye
trace( data );
in
ix
}
/l
et
}
.n
}
dn
cs
}
/b !
g.
:/ 译
lo
上面的例子中,如果socket服务器发送"Hello"字符串,则输出:
tp 翻
ht 青
//Socket接收5字节的数据:
常
72
101
108
108
111
Socket对象接收的数据都是ASCII编码的文本, 我们可以用readUTFBytes( )方法重新构造字符串,
readUTFBytes( )方法需要知道有多少个字节需要转换,用bytesAvailable属性指定字节数:
var string:String = socket.readUTFBytes(socket.bytesAvailable);
XMLSocket类和Socket类基本类似,两者都要注册监听器检测数据是否接收完毕,但是两者读取
数据的方式是不同的。
当数据准备好时 XMLSocket 实例发出 data 事件,事件类型为 flash.events.DataEvent.DATA ,它
433. 其中的 data 属性包含接收过来的数据。
从服务器返回的数据都是原始的数据,如果你希望以XML进行处理则需先把数据转换为 XML
实例。
下面的代码例子用XMLSocket连接本地服务器,端口为2900,连接成功后,发送<test>消息给服
务 器 , onData 事 件 处 理 函 数 处 理 服 务 器 返 回 的 数 据 , 返 回 数 据 为 <response><test
success='true'/></response>,注意到该事件的data属性的内容只是字符串数据,需用XML构造器
转换位XML实例,最后通过E4X 语法输出XML:
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.DataEvent;
import flash.net.XMLSocket;
public class SocketExample extends Sprite {
23
private var xmlSocket:XMLSocket;
01
ye
public function SocketExample( ) {
in
ix
xmlSocket = new XMLSocket( );
/l
et
// Connect listener to send a message to the server
.n
dn
// after we make a successful connection
cs
xmlSocket.addEventListener( Event.CONNECT, onConnect );
/b !
g.
:/ 译
lo
// Listen for when data is received from the socket server
tp 翻
ht 青
xmlSocket.addEventListener( DataEvent.DATA, onData );
常
// Connect to the server
xmlSocket.connect( "localhost", 2900 );
}
private function onConnect( event:Event ):void {
xmlSocket.send( "<test/>" );
}
private function onData( event:DataEvent ):void {
// The raw string returned from the server.
// It might look something like this:
// <response><test success='true'/></response>
trace( event.data );
434. // Convert the string into XML
var response:XML = new XML( event.data );
// Using E4X, access the success attribute of the "test"
// element node in the response.
// Output: true
trace( response.test.@success );
}
}
}
24.4. 与socket
socket
socket服务器的状态信号交换
23
01
ye
问题 in
ix
我想与服务器进行信号交换以便知道读取的数据内容是什么以及如何进行处理。
/l
et
解决办法
.n
dn
创建不同的常量来表示协议状态,用这些常量映射与之对应的状态处理函数,在socketData事件
cs
处理函数中通过状态映射表调用对应的状态处理函数。
/b !
g.
:/ 译
lo
讨论
tp 翻
ht 青
连接socket服务器需要经过一个完整的信号交换,通常服务器初始化后发送数据给客户端,客户
常
端进行回应,服务器再回应,这个完整的处理过程一直重复直到信号交换完成,这个连接才算
建好了。
就像典型的HTTP连接一样,HTTP协议定义了一系列状态代表不同的传输数据,我们现在建立
的socket连接也是最原始的无状态的,同样需要建立各种状态以及维护各种状态的函数功能。
解决办法就是创建不同的状态常量代表服务器发送的不同类型的内容,每个状态关联不同的状
态处理函数。
连接一个socket服务器需要的信号交换可能有:
当客户端连接到服务器时,服务器立即回应,发送一个整数代表服务器所支持的协议版本。
客户端返回一个整数表示可以通信的协议版本。
服务器发送8自己的认证询问。
客户端发送认证给服务器。
如果客户端的回应不合法或协议不一致或不是规定的信息则关闭连接。
435. 要实现这个信号交换过程,首先要创建代表不同状态的常量,例如建立如下常量:
public const DETERMINE_VERSION:int = 0;
public const RECEIVE_CHALLENGE:int = 1;
public const NORMAL:int = 2;
常量设置为什么值并不重要,重要的是这些常量值都应该不同,下一步就是创建代表不同状态
的处理函数,如创建 readVersion( ), readChallenge( ), 和readNormalProtocol( ),接着要把这些函
数与状态常量相关联,如下面的代码:
stateMap = new Object( );
stateMap[ DETERMINE_VERSION ] = readVersion;
stateMap[ RECEIVE_CHALLENGE ] = readChallenge;
stateMap[ NORMAL ] = readNormalProtocol;
最后就是在socketData事件处理函数中根据当前的状态调用相应的状态处理函数了,创建
currentState变量代表当前状态,根据其值调用对应的处理函数:
23
01
var processFunc:Function = stateMap[ currentState ];
ye
processFunc( ); // Invoke the appropriate processing function in
ix
完整代码如下:
/l
et
package {
.n
dn
import flash.display.Sprite;
cs
/b !
g.
import flash.events.ProgressEvent;
:/ 译
lo
tp 翻
import flash.net.Socket;
ht 青
常
import flash.utils.ByteArray;
public class SocketExample extends Sprite {
// The state constants to describe the protocol
public const DETERMINE_VERSION:int = 0;
public const RECEIVE_CHALLENGE:int = 1;
public const NORMAL:int = 2;
// Maps a state to a processing function
private var stateMap:Object;
// Keeps track of the current protocol state
private var currentState:int;
private var socket:Socket;
public function SocketExample( ) {
436. // Initialzes the states map
stateMap = new Object( );
stateMap[ DETERMINE_VERSION ] = readVersion;
stateMap[ RECEIVE_CHALLENGE ] = readChallenge;
stateMap[ NORMAL ] = readNormalProtocol;
// Initialze the current state
currentState = DETERMINE_VERSION;
// Create and connect the socket
socket = new Socket( );
socket.addEventListener( ProgressEvent.SOCKET_DATA, onSocketData );
socket.connect( "localhost", 2900 );
}
23
private function onSocketData( event:ProgressEvent ):void {
01
ye
// Look up the processing function based on the current state
in
ix
var processFunc:Function = stateMap[ currentState ];
/l
et
processFunc( );
.n
}
dn
cs
private function readVersion( ):void {
/b !
g.
:/ 译
lo
// Step 1 - read the version from the server
tp 翻
ht 青
var version:int = socket.readInt( );
常
// Once the version is read, the next state is receiving
// the challenge from the server
currentState = RECEIVE_CHALLENGE;
// Step 2 - write the version back to the server
socket.writeInt( version );
socket.flush( );
}
private function readChallenge( ):void {
// Step 3 - read the 8 byte challenge into a byte array
var bytes:ByteArray = new ByteArray( );
socket.readBytes( bytes, 0, 8 );
437. // After the challenge is received, the next state is
// the normal protocol operation
currentState = NORMAL;
// Step 4 - write the bytes back to the server
socket.writeBytes( bytes );
socket.flush( );
}
private function readNormalProtocol( ):void {
// Step 5 - process the normal socket messages here now that
// that handshaking process is complete
}
}
23
}
01
ye
in
ix
/l
et
.n
24.5. 断开与Socket
dn
Socket
Socket服务器的连接
cs
/b !
g.
:/ 译
lo
问题
tp 翻
ht 青
我想断开与服务器的连接或通知服务器断开
常
解决办法
调用Socket.close( ) 或 XMLSocket.close( ) 方法关闭连接,或者监听close事件
讨论
当我们连接一个socket连接后,用完时应该关闭连接,释放资源,如果不关闭这会导致占用端口
使其它连接无法建立。
Socket 和XMLSocket关闭socket连接的方法是一样的,都为close( ) 方法:
// Assume socket is a connected Socket instance
socket.close( ); // Disconnect from the server
调用XMLSocket实例的方法:
// Assume xmlSocket is a connected XMLSocket instance
xmlSocket.close( ); // Disconnect from the server