21. 注意Person对象的position属性指向另一个包含不同属性的对象,还发现这里的position对象
并不需要先申明。
如要在ActionScript中创建数组,先创建变量然后调用Array构造函数:
Code View:
var arr:Array = new Array("red", "blue", "white", "black", "green", "yellow");
你也可以不调用构造函数来创建数据,而是通过中括号,如:
var noConstructorArray:Array = [2, 4, 6, 8, 10, 12, 14, 16];
这跟调用Array
Array
Array构造函数效果是一样的。
1.9 . 在ActionScript
1.9节. ActionScript
ActionScript中设置变量的作用域
1.9.1. 问题
我需要有些变量可公开访问但有些防止被外部访问。
1.9.2. 解决办法
使用ActionScript的作用域修饰符。
1.9.3. 讨论
无论是在ActionScript或在MXML文件中,变量都有各种作用域。组件中的私有变量和方法
只可被其自身所访问,其他组件都无法访问。这样的定义很有用,这些数据只能有一个组件
可以修改。当你设计一个复杂类时,最好是把那些外部组件不需要的变量属性设置为私有。
公共变量对任何对象都是可见的。 应仔细考虑哪些属性是需要被外部访问以及如何限制访问
这些属性以便创建一个更好的类结构,要时刻提醒程序员注意这一特性的重要性。 对于继
承类来说私有属性也是不可见的,只有定义它的类或组件才可见。最后protected变量只被定
义它的类及其继承类所访问。
标记为私有的变量和函数只在定义它的类中可见, 保护的在任何派生类中可见,如下面的例
子使用了protected和private属性:
package oreilly.cookbook
{
public class Transport
{
protected var info:Object;
private var speed:Object;
}
}
通过使用extends
extends
extends关键字,你可以共享类属性和方法给另一个类,在下面的例子中,Car类继
承自Transport类,并继承了其非私有的所有属性和方法。Car不能访问Transport的私有属性,
否则会提示错误信息"属性没有找到"。
Code View:
22. package oreilly.cookbook
{
public class Car extends Transport
{
void
public function set data(value:Object):void
{
info = value;
speed = value.speed; /* this will throw an error
because the speed
variable is private and access to it is not allowed to any other
class */
}
}
}
Transport 类的protected属性可用于Car类,但是不能用于把它作为一个属性的其他类实例中。
两个类的任何公共属性都可以被其他类实例访问。任何指定了static
static
static关键字的类都可以不用
实例化而直接访问其属性,换句话说,static定义的变量是所有类实例所共享的。把下面这
一行加入到Car类中。
public static const NUMBER_OF_WHEELS:int = 4;
现在也直接访问NUMBER_OF_WHEELS属性而不用类实例化,例如:
import oreilly.cookbook.Car;
public class CompositionTest
{
private var car:Car;
public function CompositionTest()
{
trace " a Car object has "+Car.NUMBER_OF_WHEELS+"
trace(" "
wheels");
wheels"
}
}到目前为止,所讲的变量作用域都是与类相关的,但是不仅仅是类属性,我们还可以
在函数内创建或销毁变量,函数内的变量作用域只在函数体内有效。例如:
void
private function processSpeedData():void
{
var speed:int;
var measurement:String = "kilometers"
"kilometers";
}
在这个函数体外面,变量speed是毫无意义的,也就是说函数内任何地方定义的变量其作用
域都只能在函数体范围内。下面的代码能正确输出newCar对象:
void
private function makeCars():void
{
23. for(var
for var i:int = 0; i<10; i++)
{
var newCar:Car = new Car();
carArray.push(newCar);
}
trace(newCar);
trace
}
newCar的引用在函数返回后便会失效。
ActionScript也支持final 修饰符,指示编译器该方法不能被修改了。这个修饰符可以使你定
final
义的方法不被修改和覆盖,例如,在Transport类中定义如下方法:
Code View:
public final function drive(speed:Number):void{ /* final and cannot be overriden in
any other class*/ }
指示任何继承Transport的类将拥有此方法,其public作用域说明可被任何对象所访问,但是
Transport子类无法重新定义它。
1.10 . 在ActionScript
1.10节. ActionScript
ActionScript中创建组件
1.10.1. 问题
我想用ActionScript而不是MXML去创建组件。
1.10.2. 解决办法
创建ActionScript文件并继承一个Flex库组件。
1.10.3. 讨论
除了在MXML中创建组件为,你还可以在ActionScript中创建它们而根本不需要MXML。操
作有点不同,只需要几步。首先确定你的类正确定义包名,下面的例子中,组件所在目录是
以应用程序级目录开始,然后是oreilly/cookbook/,这就是包名称的意义:
package oreilly.cookbook
{
另一个区别是任何被包括或引入的类都必须使用全路径导入进来, 这包括任何组件即将继承
的类,例如这里的mx.containers.Canvas:
import mx.containers.Canvas;
import mx.controls.Text;
import mx.controls.Image;
import oreilly.cookbook.Person;
24. public class PersonRenderer extends Canvas
{
通常类申明后面会列出所有常量或变量。下面的例子中,类的所有私有变量都被列出。这些
属性只有组件自身可以访问,其他组件都不可以。要访问这些属性,需要提供get和set方法。
Getter和Setter方法是一种常见的用于访问私有变量的方式。
private var _data:Object;
private var nameText:Text;
private var ageText:Text;
private var positionText:Text;
private var image:Image;
在ActionScript中,构造函数总是公有的,且没有返回值,名称与类名相同,例如:
public function PersonRenderer ()
{
super();
任何将被加入到组件的组件都有其构造函数,作为addChild方法的参数被加入到显示列表
中,他们的属性可以被修改。
nameText = new Text();
addChild(nameText);
ageText = new Text();
addChild(ageText);
下面的例子中,ageText组件进行手动定位,这是必要的,因为PersonRenderer是一个Canvas,
它并不具有布局管理器,不像VBox或HBox组件。
ageText.y = 20;
positionText = new Text();
addChild(positionText);
positionText.y = 40;
image = new Image();
addChild(image);
image.y = 60;
}
如果组件已经定义了方法处理数据,比如这里的mx.containers.Canvas,你必须重写这些方法
来执行自定义行为。要重写则需使用override
override
override关键字指示编译器试图重写父类的方法, 例如 :
override public function set data(value:Object):void
{
_data = value;
25. nameText.text = value.name;
ageText.text = String(value.age);
positionText.text = value.position;
image.source = value.image;
}
override public function get data():Object
{
return _data;
}
这是最后一个方法,很原始的方法,作用域为公开:
public function retrievePerson():Person
{
/* do some special employee processing */
return null;
}
}
}
要想把这个类添加到其他组件中,可使用下面的ActionScript代码:
var renderer:PersonRenderer = new PersonRenderer();
addChild(renderer);
或者在MXML中:
<renderers:PersonRenderer id="renderer"/>
在ActionScript中构造函数是显式调用。
1.11 . 使用事件冒泡机制
1.11节.
1.11.1. 问题
我想监听从子组件传递到父组件的所有事件而不必创建一连串事件监听器。
1.11.2. 解决办法
使用Flash Player的事件冒泡机制监听从子组件传递来的事件。
1.11.3. 讨论
我们需要通过几个类来了解冒泡事件,很多类型的事件都可以冒泡:mouse-down事件,click
事件,keyboard 事件。术语“向上冒泡”指的是事件通过其自身的处理方式从显示列表传递
28. {
import mx.containers.Canvas;
import flash.events.Event;
public class CodeBehindComponent extends Canvas
{
public function CodeBehindComponent()
{
super();
super
addEventListener(Event.ADDED_TO_STAGE,
addedToStageListener);
}
void
protected function addedToStageListener(event:Event):void
{
trace("
trace " Added to Stage from Code Behind ");
}
void
protected function clickHandler(event:Event):void
{
trace " Click handled from component "+event.target);
trace("
}
}
}
在这个例子中,方法被标记为protected和private作用域相当,因为这是代码隐藏的一部分代
码,MXML将继承CodeBehindComponent类及其方法:
<cookbook:CodeBehindComponent xmlns:mx="http://www.adobe.com/2006
/mxml" width="200"
height="400" xmlns:cookbook="oreilly.cookbook.*">
<mx:Button click="clickHandler(event)"/>
</cookbook:CodeBehindComponent>
1.13 . 组件属性绑定
1.13节.
1.13.1. 问题
我创建的组件中想让其属性是可绑定的,可绑定到其他组件上。
1.13.2. 解决办法
创建getter和setter方法,用metadata标签把这些方法标记为Bindable,元数据标签里还包含当
属性被设置时其方法所发出的事件名称。
1.13.3. 讨论
当属性值发生改变时,在属性上添加Bindable元数据标签,发出相应事件,任何对象都可以
被定义为可绑定属性。最佳方法就是通过get和set函数定义这些绑定属性。当属性被设置时,
29. 一个名称和Bindable标签一样的事件发出,例如:
package oreilly.cookbook
{
import flash.events.EventDispatcher;
import flash.events.Event;
public class Person extends EventDispatcher
{
public static var NAME_CHANGE:String = "nameChange"
"nameChange";
private var _name:String;
Bindable
Bindable(event=NAME_CHANGE)]
[Bindable
public function get name():String
{
return _name;
}
void
public function set name(value:String):void
{
new
dispatchEvent(new Event(NAME_CHANGE));
_name = value;
}
}
}
Bindable标签需要属性被设置时所发出的事件名称,下面确认当Person对象的name属性值发
生改变时任何绑定的组件都能被通知到。
现在绑定属性被设置为Person对象,你可以在绑定表达式中使用任意Person实例:
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="400"
height="300">
<mx:Script>
<![CDATA[
Bindable
Bindable]
[Bindable
private var _person:Person;
public function set person(person:Person:void
void
{
_person = person;
}
]]>
</mx:Script>
<mx:Label text="{_person.name}"/>
</mx:Canvas>
1.14 . 使用自定义事件以及事件数据传递
1.14节.
30. 1.14.1. 问题
我想使用自定义事件类发出事件以及数据。
1.14.2. 解决办法
继承flash.events.Event创建类,创建与事件数据相关的属性。
1.14.3. 讨论
有时候我们希望发送事件时也附带数据对象以便监听者不用访问发出事件的对象也能访问
数据。渲染器或深度嵌入对象通过发出事件通过多个组件到达监听器并发送数据而不需要监
听组件去寻找对象及访问其组件。作为一种解决方案,需要创建一个事件类型的类,在事件
构造函数中添加需要的数据类型,记得要调用Event的super方法以便Event对象能正确被实例
化,例如:
package oreilly.cookbook
{
import flash.events.Event;
public class CustomPersonEvent extends Event
{
public var person:Person;
public var timeChanged:String;
Public function CustomPersonEvent(
false
false,
type:String,bubbles:Boolean=false
false
false,
cancelable:Boolean=false
null
null,
personValue:Person=null
""
"")
timeValue:String=""
{
super
super(type, bubbles, cancelable);
person = personValue;
timeChanged = timeValue;
}
override public function clone():Event
{
return new CustomPersonEvent(type, bubbles,
cancelable, personValue,timeValue);
}
}
}
在这个自定义Event类,继承的Event.clone方法被重写以便复制CustomPersonEvent自身。如
果事件监听器想试图重新发出自定义事件,可以这样写:
void
Private function ustomPersonHandler(event:CustomPersonEvent):void
{
dispatchEvent(event);
}
31. 这个发出的事件并不是先前收到的那个, 而是使用clone方法创建的CustomPersonEvent 一个
复本,如果clone方法没有被重新则会把CustomPersonEvent的所有属性都被复制,那时clone
方法返回的将是flash.events.Event而不会有CustomPersonEvent的任何属性。
1.15 .监听键盘事件
1.15节.
1.15.1. 问题
我想监听用户的按键,检测哪个键被按下并处理相应事件。
1.15.2. 解决办法
为应用程序的stage或组件的keyDown事件添加监听器,读取KeyboardEvents的keyCode属性。
1.15.3. 讨论
使用keyDown事件处理器监听KeyboardEvent,这些类都扩展自UIComponent。KeyboardEvent
类定义了一个keyCode属性用于存储用户按下的键码,例如:
Code View:
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="400"
height="300"
keyDown="keyHandler(event)" backgroundColor="#0000ff">
<mx:Script>
<![CDATA[
import flash.events.KeyboardEvent;
void
private function keyHandler(event:KeyboardEvent):void
{
switch(event.keyCode){
switch
case 13:
trace " Enter pressed ");
trace("
break;
break
case 32:
trace " Space Bar pressed ");
trace("
break
break;
case 16:
trace " Shift Key pressed ");
trace("
break;
break
case 112:
trace " F1 pressed ");
trace("
break;
break
case 8:
trace " Delete pressed ");
trace("
break;
break
}
}
32. ]]>
</mx:Script>
<mx:Button label="One"/>
</mx:HBox>
请注意这个类,只有当button被激活时才会监听到它发出的事件。如果你删除了button,那
么就没有东西可以激活了,keyHandler函数也永远不会被调用。要想在程序中捕获所有的
KeyEvents事件而不管有没有组件被激活,请添加下面的句子:
Code View:
addedToStage="stage.addEventListener(KeyboardEvent.KEY_DOWN, keyHandler)"
1.16 . 定义方法参数
1.16节.
1.16.1. 问题
我想定义一个方法,其参数有默认值或null值,以便调用方法时不必每次都进行传值。
1.16.2. 解决办法
在方法申明时直接对方法参数进行赋值,赋予默认值或null值。
1.16.3. 讨论
要想为方法定义一个或多个可选参数,最简单的办法就是为参数对象设置为默认值或null。
ActionScript基本类型String, Number, int, 和Boolean不能设置为null值,不过可以设置一个默
认值,例如:
public function optionalArgumentFunction(value:Object,
string:String, count:int = 0, otherValue:Object = null void
null):void
{
if(count != 0)
if
{
/*if the count is not the default value handle the value
the call
passes in*/
}
if(otherValue != null
if null)
{
/* if the otherValue is not null handle the value the
call passes in */
}
}
还有一个策略就是在变量名前使用...
...
...标记定义可选参数以及不定数量的参数。该变量将包含
一个参数数组,可被循环遍历处理。
33. void
public function unlimitedArgumentsFunction(...arguments):void
{
for each var arg:Object in arguments)
each(var
{
/* process each argument */
}
}
1.17 . 检测对象数据类型
1.17节.
1.17.1. 问题
我想检测下传入到方法的对象是什么类型。
1.17.2. 解决办法
使用is
is type
is操作符检测对象类型或者是父类对象的type
type属性。
1.17.3. 讨论
要检测一个对象的类型,ActionScript提供了is操作符,检测对象类型并返回true或false。如
果对象与测试目标一致或是其子类则返回true,比如,因为Canvas对象继承自UIComponent,
is操作符返回true。如果UIComponent测试它为Canvas类型则返回false。因为UIComponent并
不是继承自Canvas。看下面的代码:
public function TypeTest()
{
var uiComponent:UIComponent = new UIComponent();
var canvas:Canvas = new Canvas();
trace " uiComponent is UIComponent "+(uiComponent is
trace("
UIComponent));
trace " uiComponent is Canvas "+(uiComponent is
trace("
Canvas));
trace " canvas is UIComponent "+(canvas is
trace("
UIComponent));
}
输出一下内容:
uiComponent is UIComponent true
uiComponent is Canvas false
canvas is UIComponent true
类型检测最常用到的地方是当组件抛出一个事件时。 在事件处理函数中检测是什么对象发出
动作。
void
private function eventListener(mouseEvent:MouseEvent):void
{
if(mouseEvent.target is Button)
if
34. {
/* handle button specific actions */
}
else if
if(mouseEvent.target is ComboBox)
{
/* handle combobox specific things */
}
else
{
/* handle all other cases */
}
}
1.18 . 接口的定义和实现
1.18节.
1.18.1. 问题
我想创建一个接口,并创建一个组件实现这个接口。
1.18.2. 解决办法
创建一个ActionScript文件,申明此文件为一个接口,定义此接口需要的任意方法。要实现
此接口,在定义类时使用implements
implements
implements关键字。
1.18.3. 讨论
接口是一个很强大的工具,它描述一个契约,所有实现它的类都必须完全按照接口所定义的
方法包括作用域,名称,参数和返回值保持一致。反过来使用此对象的组件希望这组方法已
存在,这样只需要创建一个轻量级的类申明而不需要创建一个新类来破坏你的继承树关系。
实现接口的类也被认为是接口类型,这常被用来设置方法参数的类型或者方法的返回类型,
例如:
public function pay(payment:IPaymentType):IReceipt
这个方法接受实现IPaymentType接口的任何对象,以及返回实现IReceipt接口的对象。
接口中不能定义方法体以及任何变量,在下面的代码片段中,IDataInterface 申明和定义了5
个方法,任何实现此接口的对象都必须定义这些方法:
package oreilly.cookbook
{
public interface IDataInterface
{
void
function set dataType(value:Object):void
void;
function get dataType():Object;
function update():Boolean;
function write():Boolean;
function readData():Object;
}
35. }
要实现这个接口,申明类并添加implements标记到类申明中,所有接口中定义的方法都必须
被实现。在下面的代码中,所有接口方法被包含进来并提供函数体:
package oreilly.cookbook
{
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
public class ClientData extends EventDispatcher implements
IDataInterface
{
private var _dataType:Object;
public function ClientData(target:IEventDispatcher=null
null
null)
{
super(target);
super
}
void
public function set dataType(value:Object):void
{
_dataType = value;
}
public function get dataType():Object
{
return _dataType;
}
public function update():Boolean
{
//do the actual updating
var updateSuccessful:Boolean;
if(updateSuccessful)
if
{
return true
true;
}
else
{
return false
false;
}
}
public function write():Boolean
{
var writeSuccess:Boolean;
if(writeSuccess)
if
{
return true
true;
}
else
36. {
return false
false;
}
}
public function readData():Object
{
var data:Object;
//get all the data we need
return data;
}
}
}
如果在MXML中实现一个接口,在顶层的组件中使用implements,例如:
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="400"
height="300" implements= "IDataInterface">
44. 2.3.2. 解决办法
为用户提供一个用来选取颜色的调色板。并且使用 ColorPicker 控件的 change 事件来设定
Canvas 的背景颜色。
2.3.3. 讨论
让用户使用一个调色板。创建一个程序,使用 ColorPicker 控件来改变 Canvas 控件的
backgroundColro 属性(背景色)。 ColorPicker 控件提供给用户一种从色彩采样选取颜色的方
式。 为了实现我们的需求,Colorpicker 控件的 change 事件的处理程序被指 向到 setColor 方法 。
setColor 方法会接受一个包含了当前选中颜色的 ColorPickerEvent。具体实现代码如下:
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical">
<mx:Canvas id="cnv" width="450" height="450"
backgroundColor="#eeaeaea">
<mx:ColorPicker id="pckr" right="10" top="10"
change="setColor(event)"/>
</mx:Canvas>
<mx:Script>
<![CDATA[
import mx.events.ColorPickerEvent;
void
private function setColor(evt:ColorPickerEvent):void
{
"backgroundColor"
"backgroundColor",evt.color); }
cnv.setStyle("backgroundColor"
]]>
</mx:Script>
</mx:Application>
When the user selects a new color, the backgroundColor style of the Canvas is updated. Note that
because backgroundColor is a style attribute rather than a property of the Canvas control, the
setStyle method is used to update the style as shown:
当用户选择了一个新的颜色, Cancas 控件的 backgroundColor 样式就会更新。值得注意的是 ,
backgroundColor 是一个样式而不是 Canvas 的普通属性,所以,更新 backgroundColor 需要
使用 setStyle 方法来操作,譬如:
void
private function setColor(evt:ColorPickerEvent):void
{
"backgroundColor"
"backgroundColor",evt.color); }
cnv.setStyle("backgroundColor"