Flex入门指南—PHP开发者

来源:互联网 发布:淘宝达人有什么用 编辑:程序博客网 时间:2024/05/19 12:18

作者:Mihai Corlan—Adobe Platform Evangelist 原文:Flex for PHP developers

从上个世纪90年代末开始,我一直从事与Web相关的技术,而且我接触的第一个服务器端技术是PHP。后来,我转向ColdFusion 和Java 的开发工作,但我始终认为自己是一个PHP开发人员。当AJAX技术出现时,我开始与Prototype和 script.aculo.us等架构打交道,并且着手创建自己的架构。

2006年底,我开始体验进行Flex开发。这有点类似速成学习班,因为我需要在大约4-6个星期内为FDS((Flex Data Services, 现在称为LiveCycle Data Services)将要问世的版本创建一个演示应用程序。尽管当时我是一个Flex 和FDS的新手,但该项目进展顺利,而且我非常享受这次开发以及学习过程。

然而,在享受的同时,我感觉到Flex的不同之处。我的意思是当我在进行ColdFusion 或Java Web 的开发工作时,我并没有觉得其与PHP相比有不同之处。这仅仅是一个寻找正确的API以及适应特定语言的问题。后来,当我开始在DHTML之上进行AJAX开发时,我感觉也没有不同之处。你仍然使用相同的技巧、利用服务器端语言来创建大多数网站,以及在网站各处插入一些趣味画面(对于本案例来说,它们是一些AJAX widgets)。

当我使用Flex创建我的第一个Web项目时,我的天啊,这是一个巨大的转变。客户端和服务器之间的清晰分离(除了服务器侧的业务逻辑之外的客户端侧业务逻辑)、需要进行编译而不是翻译的客户端技术、以及客户端的两种语言等,所有这些均需要用一种不同于传统Web开发的思维方式进行思考。

这就是我写作本文的原因。我希望与你分享一些与PHP相关的Flex特定的技巧。同时,我还希望通过将Flex与PHP进行比较引入Flex概念,当然它们的比较必须是有意义的。因此本文的目标读者是:

  1. 希望学习更多关于Flex 和Action Script 3 而不只是通过简单定义能够提供的内容的PHP开发人员
  2. 已经进行Flex应用程序编码尝试并且希望更广更深地了解Flex的PHP开发人员

本文没有涉及的内容是什么?试图改变你或说服你让你相信Flex优于X或Y不是我的本意。我坚信项目具有各种各样的类型,并且利用不同的工具能够完成这些项目。

另外,本文没有为Flex 或ActionScript 3提供完整的文件资料。目前,市面上有几十本专著的数百页资料论述该主题。关于Flex的文章也有成千上万篇。而我的意图是在将Flex概念与PHP类似概念进行关联是有意义的情形下,针对最重要的议题为你提供足够的信息。为了使得本文更为实用,我对其结构进行适当的安排并且尽力避免对细节进行过多的赘述。在本文的结尾部分,我提供了Adobe AIR 的简介和一些附加参考资料,以便于你能够查阅与本主题相关的更多详细信息。

最后需要说明的是,对于本文给出的大部分范例,我选择使用Flex 3进行编程。作出这样的选择有如下几个原因:首先,在写作本文时,Flex 4仍然是beta版本。其次,由于Flex 4主要是Flex 3的演化版本,因此这里涉及的大部分应用程序能够适用于Flex4,只需进行少许更改即可。在一些范例中,我将指出这些不同之处。对于PHP范例,我选择使用PHP 5.3作为编程参考语言。前面已经给出说明,现在让我们来看看下面的本文目录,然后开始用功学习。


目录

 [隐藏]
  • 1 什么是Flex?
    • 1.1 Flex: 一个框架绑定的两种语言
    • 1.2 为什么你需要关注Flex
    • 1.3 从瘦客户端向智能/富客户端演化
  • 2 MXML语言简介
  • 3 MXML简介
    • 3.1 MXML和ActionScript 3之间的混合
    • 3.2 CSS式样
    • 3.3 在运行时修改MXML代码
  • 4 ActionScript 3语言简介
    • 4.1 隔离语句
    • 4.2 数据类型、变量和常量
    • 4.3 函数和匿名函数(闭包)
    • 4.4 OOP:类和接口
    • 4.5 继承
    • 4.6 Getters/setters
    • 4.7 接口
    • 4.8 异常
    • 4.9 对象类型的转换和测试
    • 4.10 变量作用域
    • 4.11 数组
    • 4.12 命名空间
    • 4.13 命名空间可访问性
    • 4.14 与XML的配合
    • 4.15 动态ActionScript
  • 5 Flex是异步的
  • 6 数据绑定、元数据标签和反射
  • 7 我的数据在哪里?将它显示出来!
  • 8 Flex和PHP 项目的用户授权
  • 9 建立Flex和PHP 项目
    • 9.1 Flex SDK和文本编辑器
    • 9.2 Flex Builder / Flash Builder 和 Eclipse PDT / Zend Studio
    • 9.3 调试Flex应用程序
  • 10 什么是Adobe AIR
  • 11 下一步计划是什么?
  • 12 总结
    • 12.1 Flex之旅
    • 12.2 书籍
    • 12.3 Web站点

什么是Flex?

最简单的回答是:Flex仅仅是创建Flash应用程序的另一种方法。Flash应用程序是在SWF文件中编译的,并且由Flash Player在浏览器中进行播放。为什么我们需要使用另一种方法创建Flash应用程序?传统的Flash应用程序是使用Flash制作工具创建的。如果你查看一下该工具,你会发现它主要适用于设计人员。其中包括一个舞台(stage),一条时间轴线(timeline)以及各种绘画工具等。

当你在开发应用程序并且追求开发效率时,你需要各种组件,你希望通过重用代码尽可能地提高开发效率,最后但并不是最不重要的一点是,你需要一个新型的IDE。

因此,一种修正的回答可能是:Flex是一种能够帮助开发人员快速创建富因特网应用程序(Rich Internet Application)的开源框架,这些应用程序能够在Flash Player中运行。在2006年的今天,随着Flex 2、Flash Player 9和 ActionScript 3的推出,该框架已经趋于定型。目前的版本是Flex 3,2010初,其下一个版本Flex 4将要面世。

flex_php_0.png

Flex: 一个框架绑定的两种语言

在Flex之下,你将发现下列语言和工具:

  • 两种语言:MXML和ActionScript 3。Flex提供两种语言以便创建Flex应用程序。在下面的章节中,我将进一步地讨论每种语言。
  • 一个富组件库
  • 各种编译器和调试器
  • 用于编译和调试Flex应用程序的各种命令行工具

由于Flex是一种开源框架,因此我极力推荐你访问该计划的主页并且下载相关的SDK。你能够从组建库中查阅到所有组件的源代码,你能够查询相关的开放程序缺陷和功能库(http://bugs.adobe.com/flex),以及查看各种规范的wiki页面。

Flex提供的部分效率改善应该归功于其海量组件库。该组件库包含了所有你能够想象的UI组件(例如,各种输入框(input box)、面板(panel)、视窗(window)、滑动条(slider)、数据网格(data grid)、组合框(combo box)、折叠显示(accordio)和Tab设置(tab set)等等)。另外,该组件库还包含各种布局容器和表单元素。在下面的图中,你能够看到Flex 3可以提供的各种UI组件的屏幕截图(点击可以放大)。

flex_php_1.png

如果这些组件仍然不够使用,则你可以进入相应的源代码,扩展这些组件以创建你自己的组件或从零开始创建新的组件。

为什么你需要关注Flex

在深入探讨什么是Flex之前,让我们暂停一下,重温为什么你需要关注Flex的原因。

传统的HTML Web应用程序具有“请求-响应”架构。在浏览器发送一个请求之后,相应的服务器回送一个页面,这样的周期不断重复。HTML 和 CSS为呈现该信息提供了卓越的选择, 正如可提出证据加以证明的那样,这是目前最佳的选择之一。然而,随着时间的推移,这种架构不仅局限于静态呈现,而已经扩展至应用程序平台。利用脚本技术,我们尝试创建动态页面以及设计服务器使得其能够对特定请求给出响应。更进一步地,通过添加DHTML 和AJAX ,我们能够将新的气息带入由Web服务器支持的页面:用户能够与载入页面互动并且在无需刷新整个页面的情形下改变相关的视图。

随着这些技术的演化,各种复杂的应用程序不断出现。一些Web应用程序已经开始复制许多桌面应用程序的功能而且同时能够保持Web应用程序的便利性(只要有浏览器和因特网连接的地方就能够使用Web应用程序)。因此,各种在线版本的电子制表软件和文本编辑器不断涌现。

然而,从可用性的角度来看,在线应用程序的用户友好界面要比桌面应用程序差。同时,如需创建这些复杂的Web应用程序,你必须掌握许多技术(JavaScript、DHTML、CSS、AJAX 库以及服务器端等技术 )的各种技巧,并且你必须具有处理不同浏览器之间差异的经验以及了解他们是如何实现HTML/CSS/JS的。因此,2002年,Macromedia提出了术语RIA(Rich Internet Applications)来描述一种结合了Web应用程序的优势和桌面应用程序的益处的新型应用程序。能够实现这一新型应用程序的技术就是Flash Player。

通常,如果你希望创建应用程序(而不仅仅是一个Web站点或Web页面),你可以使用Flex来实现。一些应用程序几乎不能使用HTML/JavaScript来创建,而另外一些应用程序很难跨浏览器一致运行。Flash Player 能够提供最好的图形引擎,它已经安装于98% 的具有因特网连接的计算机,并且能够将声音和图像视为“一等公民”。它支持麦克风(microphone)和Web摄像(webcam)功能、支持流媒体和数据推送(streaming and data pushing)服务、以及提供卓越的字体排印(typography)功能,其支持的功能可以填满很长的清单。

请看看下面三个应用程序以便了解使用Flex能够出现什么样的奇迹:

  1. SumoPaint是一个免费使用的图像编辑应用程序;
  2. Mindomo是一个思维导图应用程序;
  3. Times Reader是一个源自《纽约时报》(The New York Times)的应用程序。

随着时间的推移,其他技术已经进入RIA领域。除了使得如Gmail 或 Google Spreadsheets 等应用程序成为现实的AJAX 不断发展之外,今天,我们还能够看到源自Microsoft的Silverlight应用程序以及源自Sun的JavaFX应用程序。

从瘦客户端向智能/富客户端演化

让我们回到浏览器以及Web应用程序是如何传送的。当浏览器发出一个请求,相应的服务器将使用静态内容(HTML/CSS/JS 代码)和脚本(这些脚本可能查询数据库或调用其他脚本,但最终他们会输出HTML/CSS/JS)的组合来准备一个页面。该页面由浏览器载入并且显示出来。在此,一个关键元素是,通常该页面(或响应)具有显示标记(presentation markup )以及烘焙至相同消息的数据。

当应用程序的新状态需要呈现时,浏览器发出一个新的请求,之后相应的服务器将准备请求的页面。而客户端“仅仅”显示该数据。

Flex应用程序以不同的方式工作。服务器发送已编译过的Flex应用程序 (SWF 文件) ,该应用程序能够在浏览器中使用Flash Player插件运行。通常,该SWF文件只能支持客户端侧的业务逻辑。如果需要数据(例如来自数据库),则Flex应用程序能够发送请求以获得这些数据。服务器只发送数据(数据的格式可以为XML、JSON或AMF3),并且客户端能够知道如何显示该数据。我们这里讨论的是面向服务的架构:

Flex应用程序实际上是客户端–一种能够使用由服务器提供的数据服务的客户端。该应用程序能够在浏览器不刷新页面或不重新装载SWF文件的情形下变更状态。该应用程序是一种能够不仅仅显示数据的客户端。因此,通过利用Flex 和Flash Player能够创建几乎任何对象,这些对象对于在Web上部署各种游戏、应用程序以及widget具有重要意义,而这些游戏、应用程序以及widget能够与“古典”Web应用程序以及其他应用程序进行集成。

到此为止,已经谈论太多的枯燥理论,让我们看看一些程序代码吧!

MXML语言简介

MXML是一种陈述性的、基于XML的语言。在Flex应用程序中,你可以使用MXML语言快速地创建应用程序的结构/外观。在Flex中,你使用MXML能够创建的应用程序,也可以使用ActionScript 3来创建。然而,反向操作不能成立。

如果你能够使用ActionScript 3来创建可以使用MXML完成的应用程序,那么为什么MXML会首先出现?通常学习和理解利用XML语言描述的用户界面比利用命令式语言描述的用户界面要简单得多。这样利用XML语言编写的用户界面将会具有较少的代码。另外,为陈述性语言建立工具比为命令式语言也简单得多。下面是利用MXML实现的一个范例“Hello world!”。

  1: <Label text="Hello World!" fontSize="14" color="red" x="100" y="50"/>

在这段代码中,我使用了一个名称为Label的Flex组件在屏幕上显示若干文本字符。我将文本的属性设置为我期望的文本格式。另外,我希望能够对外观及该Label在屏幕上的位置进行(少许)客户化处理。因此,我使用了属性fontSize、color、x 和 y。我相信你会同意这是非常易于理解和学习的范例。

现在,考察一下利用ActionScript 3实现的相同范例:

  1: var myLabel = new Label();  2: myLabel.text = "Hello World!";  3: myLabel.setStyle("fontSize", "14");  4: myLabel.setStyle("color", "red");  5: myLabel.x = 100;  6: myLabel.y = 50;  7: addChild(myLabel);

我使用了7行代码来实现前面我仅用包含一个标记和若干属性的MXML实现的范例!现在,可以设想一下在一个真正的应用程序中,你采用大量的控件,并且在不同的容器中进行分组。那么,维护利用MXML编写的代码要比维护使用几百行ActionScript 3编写的代码更为简单。

尽管你能够使用MXML描述你的应用程序,但你不能使用MXML实现你的应用程序的业务逻辑。而你可以利用ActionScript 3实现这一目的。

Flex应用程序能够运行于Flash Player,而Flash Player只能支持ActionScript 2 和ActionScript 3。这意味着你在应用程序中编写的任何MXML代码必须通过相应的MXML编译器转换成ActionScript 3代码。然后,该代码通过ActionScript编译器被转换成Flash Player支持的字节代码(bytecode)(SWF文件)。

因此,几乎每个MXML组件的后面均存在一个ActionScript 3类(实际上,有些MXML标记没有相应的ActionScript 类,例如Script 和 Model)。在下面的范例中,这是一个源自Label 类的一个代码片段( snippet ):

  1: public class Label extends UIComponent  2:                    implements IDataRenderer, IDropInListItemRenderer,  3:                    IListItemRenderer, IFontContextComponent  4:  5: {  6:     /**  7:      *  Constructor.  8:      */  9:     public function Label() 10:     { 11:         super(); 12: 13:         // this is so the UITextField we contain can be read by a screen-reader 14:         tabChildren = true; 15:     } 16: 17:     /** 18:      *  @private 19:      *  Flag that will block default data/listData behavior. 20:      */ 21:     private var textSet:Boolean; 22: 23: ... 24: }

在任何Flex应用程序中,你应该至少具有一个MXML文件,这是主应用程序。例如,下面是“Hello World!” 应用程序的完整代码。

  1: <?xml version="1.0" encoding="utf-8"?>  2: <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">  3:         <mx:Label text="Hello World!" fontSize="14" color="red" x="100" y="50"/>  4: </mx:Application>

根节点必须总是Application,而且这是定义命名空间的位置。在本范例中,我仅仅提供一个用于MXML语言和Flex组件的命名空间:mx(上面的代码是使用Flex 3编写的。如果使用Flex 4,则代码具有微小的差异,其中声明了多个命名空间)。

如果你编写自定义组件,则必须为其添加一个命名空间。例如,在下面的代码中,我声明了第二个命名空间指向我所创建的所有组件(在本范例中,我使用了一个我自己创建的名称为MyCustomLabel 的自定义label 组件):

  1: <?xml version="1.0" encoding="utf-8"?>  2: <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*">  3:     <mx:Label text="Hello"/>  4:     <local:MyCustomLabel text="world!"/>  5: </mx:Application>

到目前为止,你也许想知道Flex应用程序是如何处理不同页面的。对于HTML Web站点,通常不同状态是在不同页面实现的。而Flex应用程序与桌面应用程序非常相似。这意味着你只需使用一个MXML文件,即可在该页面上显示应用程序的不同状态。Flex能够提供许多手段来实现这一目的,这些方法包括使用如折叠显示(Accordion)、Tab设置浏览器(Tabsets navigator)和牌布局管理器(Card layout)的Flex组件以及Flex模块等。

你已经看到MXML代码能够用于定义应用程序的外观。但你也能够利用它并且通过扩展现有Flex组件创建自定义组件。让我们来看一个范例。假设在你的应用程序中具有若干表单,这些表单具有两个按钮:Save 和Cancel。观察一下该MXML自定义应用程序的代码(该代码是在一个名称为FormButtons.mxml的文件中创建的。所有MXML文件必须使用mxml 扩展名)。

  1: <?xml version="1.0" encoding="utf-8"?>  2: <mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="150">  3:     <mx:Button id="saveButton" label="Save"/>  4:     <mx:Button id="cancelButton" label="Cancel"/>  5: </mx:HBox>

当你创建自定义应用程序时,你能够选择需要扩展的任何组件(但你不能对Application进行扩展)。我选择扩展HBox (Horizontal Box),这是一个能够在相同行显示所有children的容器。在该容器的内部,我添加了两个按钮,一个用于Save,另一个用于Cancel。我还为每个按钮设置了id 属性。你可以使用id 属性值作为引用代码其他部分的对象的一种方式。这与在ActionScript代码中声明一个变量的方式是相同的。 现在,让我们看看你如何在一个Flex应用程序中使用该自定义组件:

  1: <?xml version="1.0" encoding="utf-8"?>  2: <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*" layout="horizontal">  3:     <mx:TextInput width="150"/>  4:     <local:FormButtons/>  5: </mx:Application>

你也许会认为在MXML中你只能使用视觉Flex组件(即最终能够被显示的组件)。实际上,这是不确切的。你可以使用表示数据的MXML标签(用于存储数据的对象)或操作数据的组件(能够从服务器查询数据或向服务器发送数据的组件)。下面,你能够看到一个generic对象组件的范例,该组件具有一个名称为name的特性(property):

  1: <mx:Object id="myObj" name="Mihai Corlan"/>

正如我前面所述的那样,与Flex相伴的所有(几乎所有)Flex组件均具有ActionScript类,该类能够实现视觉外观(如果有的话)以及相应的逻辑。当你选择使用ActionScript 而不是 MXML创建一个自定义组件(不论其是否是视觉组件)时,你必须记住存在以下限制:在不带自变量的情形下,该类的构造器(constructor)必须能够被调用(如果具有自变量,则这些自变量应该具有默认值)。

MXML简介

MXML和ActionScript 3之间的混合

返回到自定义组件FormButtons (具有两个按钮的组件),你也许注意到一个问题:如果你希望在一个Save 和 Cancel 标签不起作用的地方使用该组件将会怎样?你应该创建另一个带有你期望的标签(例如Show 和 Hide)的自定义组件吗?当然,这是一个选择,但这个组件不能缩放或外观不太优雅!你所真正期望的是一个更为通用的组件,以及一种使用代码变更组件的方法。这是为什么迟早你不得不在MXML代码中添加ActionScript代码。

在下面的范例中,我在组件的MXML代码中添加了ActionScript代码以定义两个存储了按钮使用的标签的变量。注意,我使用了一个新的标签Script,并且在该标签之中使用了CDATA。这是因为在XML文档之中,像>、<和& 等字符是非法的(如果它们没有换码)。另外,现在我将不会把太多重点放在ActionScript代码上,但我将在后面的章节中更深入地讨论ActionScript代码。

  1: <?xml version="1.0" encoding="utf-8"?>  2: <mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="150">  3:     <mx:Script>  4:         <![CDATA[  5:             public var label1:String = "Save";  6:             public var label2:String = "Delete";  7:         ]]>  8:     </mx:Script>  9:     <mx:Button id="saveButton" label="{label1}"/> 10:     <mx:Button id="cancelButton" label="{label2}"/> 11: </mx:HBox>

我定义的变量可以在使用该组件的Flex应用程序中设置。下面,让我们来看看在使用新的自定义组件之后,变更的Flex应用程序代码的构成。

  1: <?xml version="1.0" encoding="utf-8"?>  2: <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*" layout="horizontal">  3:     <mx:TextInput width="150"/>  4:     <local:FormButtons label1="Show" label2="Hide"/>  5: </mx:Application>

注意,FormButtons 标签具有两个属性: label1和 label2。这是为什么你能够在这些按钮上设置期望显示的文本。并且这是你使用的机制以便将更多的行为添加至MXML组件(使用ActionScript代码)。在真正的应用程序中,你可能希望将一个行为与每个按钮进行绑定,以便当按下按钮时,就会产生相应的结果。你可以使用ActionScript代码编写通过按下按钮即可触发的各种函数。

另外,还有一种在MXML至添加ActionScript 代码的方法。你可以先创建一个ActionScript 文件(在本范例中,该文件的名称为buttons.as),然后将该文件包含于MXML文件中。你可以通过添加一个具有source 属性的 Script 标签实现这一操作 ,而该属性指向该ActionScript文件。下面是这一方法的代码。

  1: // ActionScript file called buttons.as  2: public var label1:String = "Save";  3: public var label2:String = "Delete";      1: <?xml version="1.0" encoding="utf-8"?>  2:   3: <mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="150">  4:     <mx:Script source="buttons.as"/>  5:     <mx:Button id="saveButton" label="{label1}"/>  6:     <mx:Button id="cancelButton" label="{label2}"/>  7: </mx:HBox>

现在,让我们后退一小步,然后推测一下当MXML编译器解析FormButtons.mxml 文件时会发生什么情况。你已经知道所有代码将会转换成ActionScript代码。但对于我添加的现有ActionScript代码(两个变量)会发生什么情况呢?MXML编译器将会把每个MXML文件编译至一个ActionScript 类。在这种情形下,我将获得一个名称为FormButtons(因为这是该文件的名称并且它可以用于该类的名称)的类,该类扩展了HBox 组件(因为我选择了 HBox 作为该组件的根节点)。该类中的所有ActionScript代码将会变成该类的成员:变量(例如本范例中的变量)将会变成实例变量,并且函数将会变成实例方法。

CSS式样

到目前为止,你也许想知道你是否能够改变视觉Flex组件的外观。Flex组件具有类似HTML CSS的技术?答案是肯定的,Flex 支持 CSS。在Flex 4中,对CSS的支持已经扩展至允许不仅可以基于类的名称而且可以基于ID进行式样定义,以支持伪选择符(pseudo-selector)(例如,对于一个按钮来说,你应该具有下选择符(down selector)、上选择符(over selector)等等)以及其他更多功能。

与在HTML中一样,式样可以在行中(在MXML代码内部)定义或在一个独立的文件中定义。让我们先回到自定义组件FormButtons, 然后设置若干式样。如果你选择在一个独立的文件中定义试样,则必须使用一个Style 标签并且将路径设置为 source 属性中的式样文件。

  1: <?xml version="1.0" encoding="utf-8"?>  2:   3: <mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="150">  4:     <mx:Style>  5:         .Button1 {  6:             font-size: 14;  7:             color: #990000;  8:         }  9:     </mx:Style> 10:     <mx:Script source="buttons.as"/> 11:     <mx:Button id="saveButton" styleName="Button1" label="{label1}"/> 12:     <mx:Button id="cancelButton" label="{label2}"/> 13: </mx:HBox>

我创建了一个名称为Button1 的CSS类,该 CSS类定义了标签颜色以及字体大小。然后,我使用第一个按钮的styleName 属性将式样设置为第一个按钮。至此,该应用程序如下图所示:

CSS式样在运行时能够改变(在Flex应用程序载入浏览器之后),并且该应用程序的外观也会立即随着改变。

在Flex 4中,Adobe添加了名称为MXML用于图形的新语言,该语言添加了原语(primitive)、效果(effect)、面具( mask) 和2D 转换功能。你可以使用这些新的标签在MXML文件中创建皮肤(skin)类,然后你能够将该类设置为你期望对其进行皮肤粘贴的组件。在下图中,你能够看到一串以Flex 4为皮肤的图像。左边给出了该串图像的默认状态,而右边给出了该串图像的打开状态。你可以点击这里 观看处于运行状态的应用程序。

当你在关于Flex应用程序外观的自定义的文档中徘徊时,你也许能够理解术语“皮肤粘贴”的含义。你可以进行图形皮肤粘贴(graphical skinning )或编程皮肤粘贴(programmatic skinning )以便改变相应的外观。点击这里 可以获得关于这方面问题的佳文。

在运行时修改MXML代码

有时候你希望在运行时变更UI组件。也许你希望根据你从服务器中检索到的数据来实时地建立一个表单。同样,你也能使用ActionScript代码实现这一任务。任何视觉Flex组件均具有添加一个新子类、移除一个新子类以及获取所有子类等方法。如果你愿意,你可以将这些方法与在HTML中使用JavaScript改变DOM的方式进行比较。然而,它们之间存在差异:利用JavaScript你可以注入你从服务器检动态索到的HTML代码(通过一个AJAX 调用)。但在Flex中,这是不可能,并且在Flex中不存在eval() 函数。然而,在主应用程序载入之后,有方法可以载入其它Flex应用程序或Flex模块。

当你了解一个组件的所有不同视图时,你能够使用MXML States 来实现相同组件或应用程序的不同状态。状态功能是Flex 4对比Flex 3的实现方式改善非常显著的地方,这使得这些状态更加易于使用并且添加更多功能。在Flex 4,状态的工作方式如下:

  1. 你可以定义若干状态并且指定默认状态
  2. 你能够指定在一个特定的组件应该出现在哪些状态中
  3. 你可以指定在状态出现的位置每个状态的任何属性的独立值

假设你希望创建一个处理应用程序登录功能的组件。你希望使用该组件显示登录表格,并且在登录成功之后,你希望显示退出按钮以及用户姓名。 下面是使用Flex 4创建的登录/退出组件:

  1: <?xml version="1.0" encoding="utf-8"?>  2:   3:     <s:states>  4:         <s:State name="notlogged"/>  5:         <s:State name="logged"/>  6:       7:  8:     <s:TextInput includeIn="notlogged" text="user name"/>  9:     <s:TextInput includeIn="notlogged" text="password"/> 10:     <s:Button includeIn="notlogged" label="Login" click="{currentState='logged'}"/> 11: 12:     <mx:Label includeIn="logged" text="Current user: Mihai Corlan"/> 13:     <s:Button includeIn="logged" label="Logout" click="{currentState='notlogged'}"/> 14:      15:         <s:HorizontalLayout/> 16:      17: </s:Group>

上面的代码应该是很容易理解的。作为顶部容器,我使用一个具有设置为HorizontalLayout 布局的Group组件(这与用于上面范例Flex 3中的Hbox具有相同的效果)。我已经在该文件的顶部为该组件定义了可用状态。接下来,我还定义了按钮、文本输入和标签。注意一下指定该组件在什么状态下会出现的includeIn 属性。另外,文件中还包含一个excludeFrom 属性。如果你希望在所有状态下定义一个按钮,则无需为这两个属性指定任何状态。最后,你可以看到我为这两个按钮的点击属性指定了一个表达式。例如,表达式click=”{currentState=’logged’}” 告诉Flex 当按钮被点击时,应该将该组件的状态改变为名称为logged的状态。

尽管我一直在讨论MXML语言,但我已经将越来越多的ActionScript代码加入了这些范例。这是一个迹象:该到讨论第二种Flex语言ActionScript 3的时候了。

ActionScript 3语言简介

ActionScript 3是一种动态的面向对象的脚本语言,它是(或几乎是)一种类型安全的语言。ActionScript 3 基于 ECMAScript 3 规范 (ECMA-262)。另外,其某些功能也是符合ECMAScript 4的相关建议。我已经发现采用下列方式对完全是ActionScript 3新手的开发人员解释该语言是最容易的:ActionScript 很像 JavaScript 和 Java的一个混合语言,并且其具有很强的个性。实际上,JavaScript是另一种基于ECMAScript 规范的语言,因此,其与ActionScript自然而然地具有许多共同点。

正如我前面所述的那样,Flash Player 能够运行两种语言:ActionScript 2 和ActionScript 3。Flash Player在其内部使用两种不同的虚拟机(Virtual Machine)来协调这两种语言(ActionScript 3 和 AVM2 Virtual Machine 是在 Flash Player 9中推出的)。ActionScript 3 是由核心语言(关键字和数据类型等 ) 以及 Flash Player API (该API 使得开发人员能够通过显示列表API( display list API)、3D API、绘画API(drawing API)以及动画(Animation)等API使用所有Flash Player 功能)构成的。在本文中,我将侧重讨论核心语言。点击 这里 ,你可以获得一篇关于ActionScript 3的入门佳文。

从现在开始,我将使用缩写“AS3” 替换 “ActionScript 3”。

隔离语句

在PHP语言中,你可以使用分号(;) 来隔离或结束一条语句。而在AS3语言中,你可以使用分号(;) 或仅仅行尾符。但我必须说,当我看到不用分号编写的代码时,我的眼睛将不会出现喜悦的目光。因此,我建议你使用与PHP使用的相同方法。

数据类型、变量和常量

PHP具有下列数据类型:布尔(Boolean)、整数(integer)、浮点数( floating point number)、字符串( String)、数组(Array)、对象( Object)、资源( Resource)和 NULL。

对于AS3来说,我们具有下列数据类型:

  • 顶级数据类型:布尔(Boolean)、整数(int)、单元(uint)、数( number)(与PHP的浮点数相同)、字符串( String)、Null (仅包含一个值:null)和void (仅包含一个值:undefined)
  • 复杂数据类型:对象( Object)、数组(Array)、矢量(Vector )(以 Flash Player 10开头)、字典(Dictionary)、位图(Bitmap)、字节数组(ByteArray)、日期(Date)、XML、XMLList、函数(Function)、错误(Error)和RegExp等。

在AS3语言中,一个变量就是与实际值关联的一个标识符或指针。AS3中的合法值可以是对象(int 或 uint 是对象,Number 或 Date也是对象)、null 和undefined。null 和undefined均表示数据缺失,但它们之间存在差别。当你声明一个变量并且你没有对其初始化时,并且如果变量的类型不是Boolean、int、uint或 Number,则该变量具有值null 。如果变量没有赋予数据类型并且没有初始化,则该变量具有值undefined。同时,当你拥有一个动态对象并且希望检查一个特定的方法或特性(property)是否被定义时,你可以对undefined进行检查。

在PHP中,你可以按照下面的方式声明一个变量:

  1: $anInteger = 12;  2: $isTrue = true;  3: $aString = "my string";  4: //or  5: $aString = 'my string';

在AS3中,你可以使用var 关键词声明一个变量:

  1: var anInteger:int = 12;  2: var isTrue:Boolean = true;  3: var aString:String = "my string";  4: //In AS3 you can not use simple quotes for declaring a String

注意在该变量名称之后,我添加一个类型注解;例如,myVarName:Boolean (该类型是使用 “:” 声明的,后面紧跟相应的类型)。在AS3中,你可以使用类型注解,也可以不使用类型注解(如果编译器设置为严格模式(strict mode ),则你必须使用类型注解)。

对于PHP来说,你不必声明变量的类型,因此,这一点也许显得奇怪,而且你可能希望坚持使用不指定类型的方式编写程序。由于使用类型注解具有诱人的优点,我强烈推荐你使用类型注解。首先,当你使用IDE编写代码时,对变量进行分类将使得你在编译时发现更多错误。例如,设想具有一个单一String自变量的函数。如果你试图调用该函数,将一个Object作为自变量进行传递,则IDE将提醒你这是错误。

如果不使用类型注解,你可能会遇到运行时错误,但只能在你或最终用户运行该应用程序时才会发现,并且此时查明软件缺陷的根源将会非常困难。

坚持使用类型注解的第二个原因是,如果AS3编译器知道变量的特定类型,则其能够对应用程序进行优化。

在PHP中,你能够利用每次赋值改变变量的类型:

  1: $myVar = 12; //it is an int  2: $myVar = "now is a string";

在AS3中,只有当你使用“*”声明变量为untyped 时,你才能够完成相同当操作(使用严格模式)。

  1: var myVar:int = 12;  2: //this will raise an error and the application can not be compiled  3: myVar = "this is a string";  4:  5: //declaring the variable untyped you can change the type with each assignment  6: var myVar2:* = 12;  7: myVar2 = "this is a string now";

你可能注意到只有当声明变量时我才使用关键词var。对于下一步赋值操作,你可以忽略var 和类型注解。

正如我前面所述的那样,在AS3中变量仅仅是实际值的指针。然而,当你将int、uint、Number、Boolean或 String 变量赋值给另一个变量时,将会创建一个副本(对于将这些类型的一个变量传递给一个函数时,情况也是相同的)。在PHP中,你可以使用 “&” 运算符通过引用为原语类型进行变量赋值;当你为一个变量改变值时,其它变量将指向相同的改变值。

在PHP中,你使用“.” (dot)对字符串进行串接,而在AS3中,你可以使用“+” (plus)完成相同的任务:

  1: //in PHP  2: $space = " ";  3: $a = "this" . $space . "is!";  4:  5: //in AS3  6: var space:String = " ";  7: var a:String = "this" + space + "is!";

在PHP中,你可以在任何你期望的位置定义变量:例如在文件级、在函数中或在类中。在Flex应用程序中,变量只能在函数中或类级别中声明。

此外,在PHP中,你可以编写一段没有在函数中声明的过程化程序代码(procedural programming ):

  1: <?php  2:  3: $a = 1;  4: for ($i=0; $i<100; $i++) {  5:     $a += $i * $a;  6: }  7:  8: ?>

在AS3中,你不能利用函数之外的变量进行任何操作(尽管你可以在函数之外声明变量),但有一个例外,我将在Class章节讨论它。因此,如果你试图运行下面的代码,你将在编译该应用程序时出错。

  1: <?xml version="1.0" encoding="utf-8"?>  2: <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">  3:     <mx:Script>  4:         <![CDATA[  5:         var a:int = 1;  6:         for (var i:int = 0; i<100; i++) { //this raises an error  7:             a += i * a;  8:         }  9:         ]]> 10:     </mx:Script> 11: </mx:Application>

这是你为什么你可以通过重写该代码以便其能够正常运行:

  1: <?xml version="1.0" encoding="utf-8"?>  2: <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">  3:     <mx:Script>  4:         <![CDATA[  5:         var a:int = 1;  6:  7:         function calculations(a:int):int {  8:             for (var i:int = 0; i<100; i++) {  9:                 a += i * a; 10:             } 11:             return a; 12:         } 13:         ]]> 14:     </mx:Script> 15: </mx:Application>

在PHP中,可以按照下面方式声明和使用常量。

  1: //constants  2: define("CONSTANT", "Hello");  3: $myString = CONSTANT . ‘ world!’;

在AS3中,可以使用const 关键词声明常量(使用大写字母命名常量是一个惯例):

  1: static const HELLO:String = "Hello";  2: var myString:String = HELLO + " world!";

什么可以用于变量的名称呢?对于PHP和AS3来说,这一点是相似的:只要名称的第一个字符为字母或在“_”之后跟随字母、数字或下划线,该名称即为合法名称。对于两种语言来说,下面均是合法变量名称的范例:_1, _a1A, b。 在PHP中,通过了解其字符串名称,你可以使用该变量:

  1: <?php  2: $myVar = 12;  3: $varName = 'myVar';  4: echo($$varName); //print 12;  5: ?>

在AS3中,通过使用引用成员的动态方法(变量/方法),你可以获得相似的功能:

  1: <?xml version="1.0" encoding="utf-8"?>  2: <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">  3:     <mx:Script>  4:         <![CDATA[  5:  6:             var myVar:int = 12;  7:  8:             function init():void {  9:                 var varName:String = "myVar"; 10:                 trace(this[varName]); //output 12 11:             } 12:         ]]> 13:     </mx:Script> 14: 15: </mx:Application>

在该范例中,我使用了this 来引用当前对象,但你可以对任何对象使用相同对技巧。当讨论动态类时,我将用更多当篇幅来对此进行说明。

函数和匿名函数(闭包)

在AS3中,你可以利用函数实现你可以在PHP中实现的每一件事,甚至更多事情。首先,在AS3中,你可以定义自变量类型和返回类型(在PHP中,你只能从对象添加类型注解至方法)。

  1: function calculate(x:int=0, y:int=0):int {  2:     return x + y;  3: }  4: //using the function  5: var result:int = calculate(1, 2);  6:  7: function calculateAverage(...arguments):Number {  8:     var result:Number = 0;  9:     for (var i:int=0; i<arguments.length; i++) { 10:         result += arguments[i]; 11:     } 12:     return result/arguments.length; 13: } 14: 15: //using the function  16: var result:Number = calculateAverage(1, 2, 3, 4, 5, 6);

你可以将… (rest) 运算符与显示自变量进行混合,只要你将… (rest) 运算符作为自变量列表的最后一个元素放置即可:function foo(x:int, y:int, …arguments):Number {}。当你希望利用自变量中的一个变量成员创建函数时,… (rest) 运算符是非常有用的。

如果函数无需返回任何值,你可以使用void 作为返回类型。

在PHP和AS3中,你可以为自变量提供默认值。例如:

  1: //php code  2:  3: function doThing($a, $b="default value") {  4:     echo $a . $b;  5: }  6:  7: //AS code  8: function doThing(a:String, b:String="default value"):void {  9:     trace(a + b); 10: }

当然,你可以在函数中定义函数(你将在下一个代码样本中看到一个范例)。

其次,在AS3中,任何函数将被表示为Function 类的一个实例。这使得某些有趣的事成为可能:

  • 你可以创建一个文字函数并且将它赋值给一个变量,然后通过该变量调用该函数(这在PHP中也能够实现)。
  • 你可以返回一个函数作为另一个函数的执行结果。
  • 当调用其它函数时,你可以将函数作为自变量传递。
  1: var f:Function = function multiply(x:int, y:int):int {  2:     return x*y;  3: }  4:  5: trace(f(3,5));  6:  7: function giveMeAFunction():Function {  8:     return function multiply(x:int, y:int):int {  9:         return x*y; 10:     }; 11: } 12: 13: var h:Function = giveMeAFunction(); 14: trace(h(3,4));

在PHP和AS3中,你可以创建匿名函数(闭包)。在前面的代码样本中,你能够看到一个在giveMeAFunction() 中创建一个匿名函数然后将其返回的范例。

也许AS3和PHP的函数之间的最大不同点是你定义它们的方式。在PHP中,你可以在文件中定义任意数量的函数。而在AS3中,你在一个文件中只能定义一个函数,并且该函数的名称必须与文件的名称相匹配。例如,如果你定义的函数的名称为doSomeMath(),则你必须在一个名称为doSomeMath.as的文件中创建该函数。在定义函数时,你可以使用类包声明(package declaration)(你将在下一章节学习与包相关的内容)。因此当你希望创建大量工具函数,如果你不希望在大量文件中编写这些代码,则你可以创建一个单一类然后将它们定义为静态方法。

OOP:类和接口

现在是讨论PHP和AS3的面向对象编程(Object Oriented Programming )功能的时候了。在PHP中,你可以编写面向对象或程序化的代码;而AS3是面向对象的语言。

让我们从简单PHP类开始来看看语法的不同点(记住我使用PHP 5.3作为编程参考语言):

  1: namespace org\corlan {  2:  3:     class SimpleClass {  4:  5:         public $public = 'Public';  6:         protected $protected = 'Protected';  7:         private $private = 'Private';  8:  9:         function SimpleClass() { 10: 11:         } 12:         // Redefine the parent method 13:         function displayVar() 14:         { 15: 16:         } 17:     } 18: } 19: 20: //use the class like this 21: require_once('flassFile.php'); 22: $obj = new org\corlan\SimpleClass(); 23: $obj->displayVar();

在AS3中,相同的类编写如下:

  1: package org.corlan {  2:  3:     public class SimpleClass {  4:  5:         public var _public:String = "Public";  6:         protected var _protected:String = "Protected";  7:         private var _private:String = "Private";  8:  9:         function SimpleClass() { 10: 11:         } 12: 13:         // Redefine the parent method 14:         public function displayVar():void 15:         { 16: 17:         } 18:     } 19: 20: } 21: 22: //you use the class like this: 23: import org.corlan.SimpleClass; 24: 25: var object:SimpleClass = new SimpleClass(); 26: object.displayVar();

下面是它们的主要差异:

  • 对存储类的文件进行命名
  1. 在PHP中,你可以在一个文件中定义一个类,而该文件可以以任意方式命名
  2. 在AS3 中,文件的名称必须与类的名称相同 (如果类的名称为 SimpleClass,则文件的名称必须为SimpleClass.as)
  • 命名空间 vs 类包
  1. 在PHP中,你可以使用命名空间以避免类之间的名称冲突
  2. 在AS3 中,你可以使用类包;然而,例如,当你在org.corlan类包中声明一个类时,这意味着该类将会包含于Flex源文件夹的 org/corlan文件夹中。该包名可以转变为一个文件夹结构。与类访问修饰符( class access-modifier)一起使用的类包能够将该类隐藏至项目之外的类中(后面将会详细讨论)。
  • 要求/包含vs. 导入
  1. 在PHP中,通常你可以使用require_once 函数包含定义类的文件。从PHP 5 开始,你将能够定义一个__autoload() 函数,并且在该函数中包含 require_once 或 include_once,而不需在每个文件的顶部编写一个要求的文件的列表 。
  2. 在AS3 中,你可以使用导入语句来包含期望的类。然而,如果你希望从类包 org.corlan中包含所有类,你可以使用一个通配符编写该导入语句:import org.corlan.*。另一个不同之处是AS3 编译器只编译那些在你的代码中实际使用的类 (即只有在特定类的一个实例被实际创建的情形下) 。
  • 调用方法/实例成员:
  1. 在PHP中,你可以使用 “->” 运算符
  2. 在AS3 中,你可以使用“.” (dot) 运算符

现在,让我们来讨论类/方法/成员修饰符。

我将从类修饰符开始讨论:

  • PHP 具有final和 abstract类修饰符;而public是隐式类修饰符,在PHP中,所有类均为public。
  • AS3 具有public、internal、final和dynamic类修饰符。如果你没有指定任何访问修饰符(public 或 internal),则该类默认为internal,其含义为该类只能被来自相同类包的类访问。public 和 final 与 PHP具有相同的含义。abstract 在AS3中不存在,但你可以使用接口克服这一限制。Dynamic 标示相应的类通过修改现有成员或添加新的成员能够在运行时进行更改。

类特性(class property)修饰符:

  • PHP 具有 public、private、protected和static修饰符。
  • AS3 具有 PHP 修饰符加上 internal修饰符。Internal 可用于使得特性(property)只能在相同类包的内部才是可用的。当没有指定修饰符时,可以使用internal 修饰符。

类方法(class pmethod)修饰符:

  • PHP 具有 public、private、protected、static、final和abstract修饰符。
  • AS3 具有public、private、protected、static、final、internal和override修饰符。abstract 在AS3中不存在;Internal 可以使得方法只能在相同类包的内部的代码中才是可用的。

在AS3中,你可以使用函数闭包实现的每一件事均可以使用类函数实现。

PHP的构造器可以标示为private,你可以定义一个具有与类名称相同的构造器,或你可以使用特定的方法__construct()和__destruct()。在AS3中,构造器总是具有public修饰符并且必须具有与类相同的名称。如果没有提供任何修饰符,则AS3将为你在场景之后创建一个修饰符。

静态成员或静态方法是可以访问的:

  • 在PHP中,使用ClassName::propertyName。
  • 在AS3中,使用ClassName.propertyName。然而,在相同类的内部,你可以省略该类名称。
  1: package org.corlan {  2:  3:     public class Foo {  4:  5:         private static myVar:String;  6:  7:         function Foo() {  8:             Foo.myVar = "value 1";  9:             myVar = "value 2"; 10:         } 11: 12:     } 13: }

这里:

  • 在PHP中,你可以使用特别的类变量$this 来指向在相同类中定义的类成员(变量/方法): $this->myVar = 22。
  • 在AS3中,你可以使用相同的this:this.myVar = 22;然而,你可以忽略this, 然后仅使用myVar = 22即可。

在AS3中,只有一个类能够在类包的内部进行声明(一个this类可以给出该文件的名称)。然而,在类包声明的外部,你可以声明任意数量的类:

  1: package org.corlan {  2:  3:     public class Foo {  4:  5:         private static var instance:Foo;  6:  7:         function Foo(object:Bar) {  8:  9:         } 10: 11:         static public getInstance():Foo { 12:             if (Foo.instance == null) { 13:                 Foo.instance = new Foo(new Bar()); 14:             } 15:             return Foo.instance; 16:         } 17: 18: } 19: 20: class Bar {}

这将产生一个有趣的效果:所有在类包外部的文件中定义的类将只能在相同文件内部声明的代码中使用。对于所有其它代码,这些类不存在。记住AS3的限制,即你不能声明该构造器为private。因此,通过使用与该范例相似的方法,你可以确保只存在一个Foo 类的实例。如果某外部代码调用该构造器时,系统将抛出一个运行时异常,因为外部代码不能使用Bar的实例并且该类对于外部代码是不可见的。

继承

在AS3中进行类的扩展与在PHP中非常相似。你可以使用相同的extends 关键词后面跟随你希望扩展的类的名称。覆盖(Overriding)功能与PHP相同,有一点不同之处是你必须将override 关键词添加至方法签名。AS3不支持重载(Overloading)功能(你不能拥有两个或更多具有相同名称的方法)。

在PHP中,你可以使用语句parent::memberName访问父成员;而在AS3中,你可以使用语句super.memberName访问父成员。当执行一个类的构造器时,首先应该调用父构造器。甚至当你没有从你的代码中显式调用父构造器时也会发生父构造器调用。因此,当你在构造器方法中编写代码时,你不能在你的代码之后放置父构造器的调用语句。这样,你可以为父类的正确初始化提供了机会,因此子类将不会使用没有设置的成员。你可以使用语句super()调用父构造器。让我们来看看这些工作原理,先看PHP代码,然后再看AS3代码。

  1: class SimpleClass {  2:  3:     function SimpleClass() {  4:         echo('SimpleClass() called');  5:     }  6:  7:     function __construct() {  8:  9:     } 10: 11: 12:     function displayVar() 13:     { 14:         echo "SimpleClass class\n"; 15:     } 16: } 17: 18: class ExtendClass extends SimpleClass { 19: 20:     function ExtendClass() { 21:         $myVar = 1; 22:         parent::SimpleClass(); 23:         //or 24:         parent::__construct(); 25:     } 26:     // Redefine the parent method 27:     function displayVar() 28:     { 29:         echo "Extending class\n"; 30:         parent::displayVar(); 31:     } 32: }  1: public class SimpleClass {  2:  3:     function SimpleClass() {  4:         trace("SimpleClass() called");  5:     }  6:  7:     public function displayVar():void  8:     {  9:         trace("SimpleClass class"); 10:     } 11: } 12: 13: public class ExtendClass extends SimpleClass { 14: 15:     function ExtendClass() { 16:         super(); //we have to call first the parent constructor, and only after we can execute our code 17:         var myVar:int = 1; 18:     } 19: 20:     override public function displayVar():void { 21:         trace("overrided displayVar()"); 22:         super.displayVar(); 23:     } 24: }

让我们看一下在AS3中类是如何初始化的。当一个类被例示时,首先其所有的特性(property)将被初始化,其次在类级别定义的静态代码将被执行(这在PHP中是不能执行的),最后构造器将被执行。下面是范例代码:

  1: public class Foo {  2:  3:         private var a:int = 0;  4:         private static var os:String;  5:         trace("initializer");  6:  7:         if (Capabilities.os == "LINUX")  8:             os = "LINUX";  9:         else 10:             os = "other"; 11: 12:         public function Foo(a:int=1) { 13:             trace("foo() executed"); 14:         } 15: } 16: 17: var foo1:Foo = new Foo(); 18: var foo2:Foo = new Foo(); 19: //produces this output in console: 20: initializer 21: foo() executed 22: foo() executed

在AS3中,你可以使用函数的原型特性(property)创建函数闭包之外的对象(这与你在JavaScript中使用的函数来创建/扩展类的情况是相似的)。下面是一个简短范例代码:

  1: //we create a function  2: function MyClass(value:String = "Mihai") {  3:     //we create a property  4:     this.name = value;  5: }  6: //we use the special variable prototype of the function   7: //to create another method   8: MyClass.prototype.setName = function (value:String):void {  9:     //we have access to the property defined on MyClass object 10:     trace(this.name); 11:     this.name = value; 12:     trace(this.name); 13: } 14: 15: //create an instance 16: var myObject = new MyClass(); 17: //accesing the method created earlier 18: myObject.setName("Joe");

在后面的章节中,我将更多地讨论AS3的动态功能。

Getters/setters

在任何OOP语言中,你通常会使用getters/setters来控制你希望向外部暴露的类特性(property)。PHP也不例外。然而,在AS3中,可以使用关键词set 和 get对类特性(property)提供特别支持。下面是范例代码:

  1: public class Employee {  2:  3:     private var _salary:int = 0;  4:     private var _income:int = 0;  5:  6:     function Employee() {  7:  8:     }  9: 10:     public function set salary(value:int):void { 11:         if (value > 0) { 12:             this._salary = value; 13:             this._income = this._salary * 12; 14:         } 15:     } 16: 17:     public function get salary():int { 18:         return this._salary; 19:     } 20: 21:     public function get income():int { 22:         return this.income; 23:     } 24: } 25: 26: //using this class 27: var emp:Employee = new Employee(); 28: emp.salary = 1000; 29: trace(emp.income); 30: //this raise an error, because the income property is read-only  31: //for the outside code 32: emp.income = 120;

通常,尽管我使用setter 和getter替代_salary 字段 ,但我可以调用这些方法,好像它们就是字段或特性(property)而不是函数:以object.salary = 20 替代object.salary(20)。如果你选择不对setter进行定义,你可以获得只读特性(property)。这就是我使用_income 特性(property)所实现的功能。

除了使得代码简洁一些之外,该功能还使得编写可以被其它应用程序使用的API或类更为简单。假设在我的范例中,我选择创建_salary 字段作为一个public成员。如果后来我决定需要验证能够设置的值,则我必须添加一个setter。在PHP中,这可能需要使用如myObject.setSalary()的语句。此时,任何使用该类的代码将会被破坏;因此,代码必须使用setter进行更新。

在AS3中,你可以利用定义为public var salary:int 的特性(property)来启动该类,并且当你决定需要一个setter时,你可以对该变量重新命名,然后添加public function set salary() 方法。使用该类的任何代码将不受这一变更的影响,因为它仍然使用相同的语句objectInstance.salary = 10来访问该特性(property)。

在AS3中,当使用这种式样的setter 和getter时,有一个惯例是在变量名称前添加一个下划线。

接口

在PHP和AS3中,接口的工作方式几乎相同。一个显著的差异是在PHP中,你可以定义方法以及常量,而在AS3中,你只能定义方法。然而,你可以定义setter/getter:

  1: public interface IEmployee {  2:  3:     public function set salary(value:int);  4:     public function get salary():int;  5:     public function get income():int;  6: }

异常

正如在PHP中的那样,AS3能够支持异常处理:

  1: try {  2:  3: } catch(e:Error) {  4:  5: } finally {  6:  7: }  8:  9: //throwing an exception 10: throw new Error("Some error");

在AS3中,对于所有错误来说,Error 是顶级类。你可以创建自己的错误以扩展该类,或你可以使用现有的子类。

对象类型的转换和测试

有时,你希望将一个对象强制转换为一种不同的类型,或希望检查对象类型。在PHP中,你可以使用instanceof检查对象的类型,而在AS3中,你可以使用is检查对象的类型。为了进行类型强制转换,在AS3中,你可以使用两种不同的语句。

  1: class A {};  2:  3: class B extends A {};  4:  5: var b:A = new B();  6: //casting  7: var c:B = b as B;  8: //or  9: var d:B = B(b); 10: 11: //checking the type of an variable 12: if (b is A) 13:     trace(true); 14: if (b is B) 15:     trace(true);

变量作用域

前面我们已经了解了变量、函数和类是如何在Flex 和AS 3中工作的,现在是讨论变量作用域的时候了。在PHP中,通常你具有两个作用域:全局(global)(在文件级别定义的变量)和本地(local )(在函数内部定义的变量)。

在Flex中,共有5个作用域:函数体(function body)、实例方法体(instance method body)、静态方法体( static method body)、类体(class body)和全局作用域(global scope)。向这些作用域添加访问修饰符(public/private/protected/internal)使得事情比在PHP中变得更为复杂。

作用域可以嵌套。在本例中包含的作用域的变量/函数/成员均可以被嵌套的变量/函数/成员使用。例如,当你在另一个函数体的内部声明一个匿名函数时,在AS3中,所有在外部函数定义的变量可以在嵌套函数的内部使用。在PHP中,你必须传递你希望使用的变量,或添加use 语句:

  1: //php code  2: function a() {  3:     $a = 1;  4:     $b = 2;  5:  6:     function b() use ($a, $b) {  7:  8:     }  9: } 10: 11: //AS3 code 12: function a():void { 13:     var a:int = 1; 14:     var b:int = 2; 15: 16:     function b():void { 17:         //variables a and b are available here 18:     } 19: }

当你在一个未命名类包的内部声明一个函数时,其将被放置在全局作用域之中并且其对所有代码均为可用的。然而,尽管在一个类包之外声明的任何函数仍然位于全局作用域,但其只能对来自相同文件对代码是可见的。

数组

在AS3中,数组与PHP的情况非常相似,仅有一处差异:在AS3中,一个数组仅具有数字下标。如果你希望创建一个关联数组,你可以使用Object 类。如果你希望创建一个key是对象(而非字符串)的哈希图(hash map)时,你可以使用Dictionary类。你可以使用Array 类创建数组,也可以创建多维数组。对于Object 和Array ,你均可以使用文字定义。让我们来看看若干范例:

  1: var myArray1:Array = new Array(1, 2, "some string");  2: //creates an array with three elements: 0->1, 1->2, 3->some string  3:  4: //literal definition for an array  5: var myArray2:Array = [1, 2, 3];  6: //ading two more elements  7: myArray2.push(4,5);  8:  9: //a hash map, similar to associative arrays in PHP 10: var myMap:Object = new Object(); 11: myMap.name = "Flex"; 12: //literal definition of a map 13: var myMap2:Object = {name:"Flex"}; 14: 15: //using Dictionary class 16: var dic:Dictionary = new Dictionary(); 17: var anObject:Object = new Object(); //creating the key 18: dic[anObject] = "some value"; //adding a value to Dictionary

你具有添加元素或删除元素的所有期望的方法,包括push、shift、pop、unshift和 splice。Concat 可以用于将数组添加至另一个数组。在前面的范例中,你可以看到我是如何使用 push 来添加另外两个元素至一个数组。 数组具有不固定的长度。其长度随着你添加更多元素而增加。在PHP中,你可以使用“[]” 在数组的结束部分添加一个新的元素。AS3具有相似的方法,该方法使用数组的length 特性(property )(你也能够使用length 特性(property )来声明数组的长度)。

  1: var array:Array = new Array();  2: array[array.length] = 1;//array has the values: 1  3: array[array.length] = 23;//array has the values: 1, 23

你可以使用delete 将数组的一个特定元素设置为undefined:delete array[index]。这一操作将不会缩短数组的长度。你可以使用for() 语句并且利用其length 特性(property )对数组进行循环操作。如果你希望对一个Object进行循环操作(这可以用于创建与PHP关联数组相似的数组),你可以使用for – each (其工作方式与PHP中相同的构造器相似) 或 for – in 语句(在 动态(Dynamic)章节将会更多地对其进行讨论 )。

命名空间

如果你希望寻找与PHP命名空间对等的AS3概念,你应该阅读类(Classes)章节关于类包的内容,因为AS3类包与PHP命名空间是相似的。

在ActionScript中,命名空间具有不同的含义。让我们来看看命名空间可以用来干什么,然后,我将给出若干范例:

  1. 防止命名冲突(你可以在相同的类中创建具有相同名称的多个方法,每个方法存储于不同的命名空间)
  2. 向自定义visibility设置标示跨框架/程序的变量和方法(例如,Flex使用一个名称为mx_internal的命名空间;使用该命名空间而不使用private 或 protected使得使用这些来自Flex框架的跨类包和类的方法成为可能。同时,开发人员将被警告这些方法或成员不一定能够被外部使用,因为它们可能会改变)
  3. 实现基于许可的类的访问控制功能
  4. 创建能够基于特定选择的命名空间来改变其行为的类

在详细讨论命名空间之前,我们应该注意到命名空间可以被Flash Player 内部使用以实现访问修饰符:public、protected、internal和 private。

你可以使用语句namespace identifier = URI定义一个命名空间。当你声明变量/方法以及希望限定一个成员或方法以便使用它时,你应该使用标识符。URI 通常是一个 URL ,该URL对于你的应用程序来说必须是唯一的。它并不是必须存在的,并且在大多数情形下,你应该使用你的域名。例如,我可以定义一个如下形式的命名空间:namespace online = “http://corlan.org/apps/online”。

你可以在能够定义变量的任何位置定义命名空间:在类包定义的顶层(它在整个程序中均可用),或在类级别(它只能在定义它的类中可用)。在函数级别,你只能使用在其它位置定义的命名空间(你可能需要这样做以便限定一个在其它位置使用相同命名空间定义的变量;你需要知道相应的URI以便进行限定)。

你可以通过在相应声明之前放置命名空间标识符,在一个给定的命名空间中声明一个方法或变量。例如:mynamespace var a:int = 1。当你在一个命名空间中定义一个变量或方法时,系统不允许你使用其它访问修饰符(例如private )。 为了调用在一个命名空间定义的变量或方法,你可以使用命名修饰符运算符“::”。假设你在一个名称为online的命名空间中定义了一个名称为myMethod() 的方法,你就可以使用语句objectInstance.online::myMethod() 访问该方法。对变量来说,情形也是相同。有时,你可能需要使用那些利用命名空间名称限制的变量或方法。你可以在相应的作用域打开该命名空间并且摆脱该命名修饰符运算符的限制。你可以使用use namespace namespaceidentifier 指令实现这一目的。例如:

  1: public function doSomething() {  2:     use namespace online;  3:     //call the method defined in that namespace:  4:     myMethod();  5: }

你可以传送命名空间,例如,你可以从一个方法中返回一个命名空间,以便允许该调用代码使用它来限定一个方法或成员。

现在,让我们来创建两个命名空间,它们可以用于在运行时改变类的行为。首先,我将定义这两个命名空间(我将为每个命名空间提供一个文件):

  1: // ActionScript file online.as  2: package org.corlan {  3:     public namespace online = "http://corlan.org/apps/online";  4: }  1: // ActionScript file offline.as  2: package org.corlan {  3:     public namespace offline = "http://corlan.org/apps/offline";  4: }

接下来,我将使用这两个命名空间创建一个能够存储一个对象的类。根据其链接状态,该类可以在本地存储该对象(例如,使用本地存储设备),或以远端方式在服务器上存储该对象(使用REST服务)。当某些代码需要使用该类时,有趣的部分将会出现。该调用的代码完全不关心该方法,它只希望将该对象存储起来。

通过使用这两个命名空间,我将创建一个类,该类具有两个名称均为save()的方法,每个方法定义于其中一个命名空间。下一步,我将创建一个私有变量,该变量存储了当前的命名空间,根据因特网连接的状态可以使用该命名空间。调用程序可以使用getter访问当前命名空间,并且使用它来调用save() 方法。再一次强调一下,调用程序不知道所有的这些internal,而且不知道这些命名空间,它也不关心这些命名空间。让我们看一下PersistObject 代码:

  1: package org.corlan {  2:     import flash.events.Event;  3:  4:     public class PersistObject {  5:  6:         private var _mode:Namespace = offline;  7:  8:         public function PersistObject() {  9: 10:         } 11: 12:         online function save(object:Object):void { 13:             //save the object back to server 14:             trace("online"); 15:         } 16: 17:         offline function save(object:Object):void { 18:             //save the object locally 19:             trace("offline"); 20:         } 21: 22:         private function connectivityChanged(e:Event):void { 23:             //here the mode can be changed from offline to online 24:             //and vice-versa 25:         } 26: 27:         public function get mode():Namespace { 28:             return this._mode; 29:         } 30:     } 31: }

下面的代码片段使用了该类。该代码段非常简单,并且行内的注释给出了说明。

  1: //creating an object that we want to be stored  2: var object:Object = {book:"Ulysses", author:"James Joyce"};  3: //create an instance of PersitObject  4: var persistenceObject:PersistObject = new PersistObject();  5: //get the current namespace  6: var currentMode:Namespace = persistenceObject.mode;  7: //use the namespace we retrieved to qualify the save method()  8: persistenceObject.currentMode::save(object);

命名空间可访问性

你可以使用与变量或方法相同的访问修饰符:public, internal, protected和 private (对于在类包级别定义的命名空间,你可以只使用 public 和internal)。将这一点与命名空间定义的位置结合起来,你将对程序中命名空间的可见性具有很强的控制能力。

与XML的配合

在PHP中,通过本机函数或附加扩展对XML提供了大量支持功能。在AS3中,本机有两个类可以表示XML:XML 和XMLList。AS3能够基于W3C DOM (你可以使用像 children()、appendChild()、parent()、 insertChildBefore() 等方法) 实现XML类。当你利用XML进行编程时,你应该了解如何使用E4X。E4X (ECMAScript-for-XML) 是 ECMA-262 语言的一种扩展,该扩展是通过AS3实现的。你可以使用XML来表示一个XML文档。即使在只有一个子类的情形下,任何来自该文档的节点都将包含于一个XMLList。

你可以使用下列任何方法创建一个XML对象:

  1. 使用文字形式编写XML。
  2. 创建一个XML实例,然后从一个外部文件导入该XML实例。
  3. 创建一个XML实例,然后使用点记法(dot notation)以便添加/改变相应的结构:
  1: var author:XML = <author/>;  2: author.@id = 1; //setting an attribute called id and its value  3: //adding two child nodes to author:  4: author.name = "Mihai Corlan";  5: author.article = "Flex for PHP developers";  6:  7: //this code is equivalent with:  8: var author:XML = <author id="1">  9: <name>Mihai Corlan</name> 10: <article>Flex for PHP developers</article> 11: </author>;

通过使用E4X,你可以根据基于节点名称或属性值的创建条件很容易地找到节点。你可以使用递减运算符 “..” 查询具有给定名称的所有节点(例如,为了查询所有程序节点,你可以使用语句:programs..program)。你可以使用“@” 运算符 (例如, programs..program.(@id==2))创建基于属性的条件。最后,通过使用点符号,你可以在节点之间浏览(应该记住即使在只有一个子类的情形下,任何子类均被看作一个XMLList)。下面你将看到使用E4X与XML配合的范例:

  1: var programs:XML = <root>  2:     <program id="1">  3:         <name>Flex</name>  4:     </program>  5:     <program id="2">  6:         <name>ActionScript 3</name>  7:     </program>  8:     <program id="3">  9:         <name>AJAX</name> 10:     </program> 11: </root>; 12: 13: //retrieving the second program node and printing its name 14: trace(programs.program[2].name[0]); 15: //retrieving all the program nodes: 16: var list:XMLList = programs..program; 17: //retrieving all the program nodes that have an id attribute equal to 2 18: var list:XMLList = pograms..program.(@id==2);

动态ActionScript

还记得AS3的定义吗?在该定义中,我给出了AS3是一种动态脚本语言的陈述。让我们更进一步地讨论该功能。动态表示通过添加或删除方法或成员可以在运行时修改一个对象。系统能够将新的方法添加至类的本身(并且任何由该类创建的对象将具有这些方法)。你甚至可以从头创建新类(使用protoype 特性(property))。AS3具有内置的动态对象,例如Object,而Flex具有另一个动态对象例子:ObjectProx。

如果你想知道为什么该功能首先出现,其答案是非常简单的:ActionScript的早期版本不具有AS3今天提供的所有功能和OOP。我必须说明,以我的经验来看,没有多少开发人员使用AS3的动态功能。这里有下面几个原因。首先,动态成员的访问速度要慢于固定成员。其次,你的代码更易于出现软件缺陷(例如,没有编译时间错误检查)。

你可以不局限于使用内置类;你能够在类定义中使用dynamic 修饰符创建动态对象:

  1: dynamic public MyDynamicObject {  2:  3: }

现在,通过使用你刚才定义的类,你可以在运行时添加成员(记住所有动态实例变量均为untyped 和public):

  1: var a:MyDynamicObject = new MyDynamicObject();  2: a.author = "Mihai Corlan";

你可以使用for-each-in 循环语句将一个动态类的所有成员遍历一次。下面是如何从前面的范例中显示成员的代码:

  1: for each (var element:* in a) {  2:     trace(element); //displays Mihai Corlan  3: }

如果你希望获得成员名称而不是其值,你可以使用for-in 循环语句:

  1: for (var memberName:* in a) {  2:     trace(memberName); //outputs author  3:     trace(a[memberName]); //outputs Mihai Corlan  4: }

Flex是异步的

到目前为止,我们已经讨论了许多Flex功能,并且其中大部分功能与其PHP的对等部分非常相似。但是,Flex的异步特性与PHP相比具有相当的差异。停止与之对抗并且顺其自然,了解这一点非常重要。

“Flex是异步的”这句话的含义是什么?假设你创建一个Flex应用程序,并且在该应用程序载入浏览器之后,用户可以选择从另一个站点下载图片。你也许使用一个URLLoader 类来实现这一任务。当你执行load() 方法时,在代码的下一行,你将不会拥有相应数据。在load() 调用等待相应数据已经载入之后,脚本不会暂停。取而代之的是脚本的执行将被恢复。作为一个程序员,你可以使用内置的AS3事件系统处理这一异步特性。如果你熟悉AJAX编程,这与你在执行一个AJAX调用时所做的事情相似:你提供一个回调函数,并且当数据到达时,该回调函数将被调用并且你可以访问相应的下载数据。

回到ULRLoader 范例,你可以为result 事件添加一个事件侦听器。这是一个函数,当数据载入之后,它将被调用。

  1: function loadPic():void {  2:     var loader:URLLoader = new URLLoader();  3:     loader.dataFormat = URLLoaderDataFormat.BINARY;  4:     //adding the event handlers or listeners  5:     loader.addEventListener(EventComplete, picLoaded);  6:     loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, picError);  7:     //starting the loading  8:     loader.load(new URLRequest("http://some_url"));  9: } 10: 11: //event handler for  12: function picLoaded(event:Event):void { 13:     //get the data from the loader object 14:     //use the target property to get the loader object 15:     (event.target as URLLoader).data; 16: } 17: 18: //event handler for the error event 19: function picError(event:IOErrorEvent):void { 20:     //displays the error id in a pop-up windonw 21:     Alert.show(event.errorID); 22: }

我从上面代码得出的功能概述是:别调用我们,我们将调用你们!

正如我在前面所述的那样,AS3具有内置的事件系统。所有事件的顶级类是Event。所有异步工作的对象均具有addEventListner() 方法,并且前面两个自变量为事件类型,以及在事件发生时被调用的函数的名称。你可以认为只有处理检索的远端数据的对象才遵从该事件模型。实际的情况并非如此;所有可见的组件或对象也具有各种事件。例如,每个Flex应用程序具有一个creationComplete 事件。当所有应用程序要求的组件被处理并且在屏幕上绘制之后,该事件将被触发。

尽管你可能感觉这样的代码不如PHP的代码简单易懂,但对于在Flex(以及Flash Player)中随处可见的异步调用来说,这里具有一个恰当的理由:Flex是一种客户端侧技术。例如,如果所有调用均为同步调用,则应用程序的用户界面将会停止对涉及下载数据的调用的响应。而用户非常厌恶不能对用户界面及时响应。

你可以删除一些事件,甚至能够改变其默认行为。如果你愿意,我将让你自己研究这些详细内容。从现在开始,你应该对什么是事件和事件侦听器具有相当清晰的理解。

数据绑定、元数据标签和反射

数据绑定是另一个能够使得开发人员的生活更为轻松的Flex功能,并且同时能够减少代码的行数。数据绑定是一种能够将数据模型与视图进行绑定的很好方法,并且能够自动更新视图以反映任何数据模型的变化。

因为Flex可以用于创建应用程序的用户界面,因此Flex组件通常是以显示大量数据的方式结束的。当数据被修改时,甚至在实时修改的情形下,通常,人们希望显示最新的而不是老旧的数据。通过使用数据绑定功能,你能够轻松地实现这一目标。数据绑定可以将一个对象的特性(property)(称为源)与另一个对象的特性(property)(称为目的地)进行链接,因此,每当源改变时,相应的目的地将自动更新。

Flex 4支持双向绑定功能(实际上Flex 3也支持该功能,但你需要在第二步对其进行声明),这意味着绑定功能在另一个方向也能起作用:即当目的地被更新时,新的值将被复制到相应的源。当你具有一个数据模型和一个表单时,这是非常有用的。你可以将数据模型与表单进行绑定,然后当用户改变表单中的值时,双向数据绑定功能将会利用表单的值来更新数据模型。现在,让我们来看看一些代码:

  1: <?xml version="1.0" encoding="utf-8"?>  2: <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal">  3:     <mx:Script>  4:         <![CDATA[  5:  6:             [Bindable]  7:             private var labelValue:String = "Hello World!";  8:  9:             private function add():void { 10:                 labelValue += "!"; 11:             } 12:         ]]> 13:     </mx:Script> 14:     <mx:Label id="myLabel" text="{labelValue}"/> 15:     <mx:Button label="Add a !" click="add()"/> 16: </mx:Application>

labelValue变量的Bindable 元数据可以将其标示为数据绑定的源。下一步,我对Label 的文本属性使用了“{}” 符号以便将该特性(property )标示为该绑定的目的地。在设置绑定功能之后,每当变量labelValue 被改变时,标签将更新其视图以反映相应的改变。我可以针对许多标签或文本输入使用相同的变量,而且他们都能够被更新以反映新的值。

这里也存在一个具有相同功能的MXML语句:<mx:Binding source=”labelValue” destination=”myLabel.text”/>。如果你希望将该数据与一个可编辑的控件(例如,一种文本输入)进行绑定,然后复制该值到该源,那么在Flex 4中,你可以使用“@” 运算符现实双向绑定功能:

  1: <s:TextInput id="txt" text="@{labelValue}"/>

之后,如果你希望使用Binding 标签,则你可以将属性twoWay 设置为true(再一次声明,这只能适用于Flex 4):

  1: <mx:Binding source="labelValue" destination="myTextBox.text" twoWay="true"/>

为了实现数据绑定功能,Flex在编译时间添加了胶水代码(glue code )(记住数据绑定不是Flash Player 或 AS3的功能),这样做的美妙之处是你不需要自己编写该代码。

尽管数据绑定功能为数据模型与视图的绑定提供了一种简单易懂的方法,但是,如果你具有大量的变量绑定,并且它们每秒更新数百次,则会对性能产生影响。对于这样的变量,没有必要对用户界面进行如此频繁的更新,因为对浏览器自己执行的每秒帧数(大约每秒50帧)有相应的限制。因此,试图每秒实时显示数百次变更是毫无意义的。

另一个需要记住的事情是并不是所有的对象均为可绑定的。例如,Object 和Array 都不是可绑定的,因此,你应该使用ObjectProxie 和ArrayCollection。当你创建类以便为数据建立模型时,如果你希望该类的所有成员均为可绑定的,则你应该将Bindable 元数据放置在类级别之上,而不是为每个特性(property)添加Bindable 元数据:

  1: package org.corlan {  2:  3:     [Bindable]  4:     public class VOAuthor {  5:  6:         public var id_aut:int;  7:         public var fname_aut:String;  8:         public var lname_aut:String;  9:     } 10: }

现在,让我们讨论一下元数据标签(有时称为注解)。你已经看到了以Bindable 元数据标签的形式出现的元数据标签。如需获得你能够在Flex中使用的元数据标签的完整列表,请点击这里。在一些情形下,MXML编译器可以使用元数据标签以便产生胶水代码(如在Bindable的情形下),而在另一些情形下,你可以使用元数据标签来向Flash Builder IDE发送示意,或在MXML标签中创建特性(property)。这是Event 元数据的情形。例如,假设我编写一个类,它能够在载入一个电影时抛出一个事件。我可以使用Event 元数据声明该事件的类型和名称。之后,我可以使用MovieLoader MXML 标签的movieLoadedEvent 特性(property)来为该事件注册事件侦听器。让我们看看该类的代码以及如何在MXML中使用该类。

  1: //class definition  2: package org.corlan {  3:     import flash.events.EventDispatcher;  4:     import flash.events.IEventDispatcher;  5:  6:     [Event(name="movieLoadedEvent", type="flash.events.Event")]  7:  8:     public class MovieLoader extends EventDispatcher {  9: 10:         public function MovieLoader(target:IEventDispatcher=null) { 11:             super(target); 12:         } 13: 14:     } 15: }  1: <?xml version="1.0" encoding="utf-8"?>  2: <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"  3:         xmlns:local="org.corlan.*" layout="horizontal">  4:     <mx:Script>  5:         <![CDATA[  6:             private function movieLoadedListener(event:Event):void {  7:                 //do something with it  8:             }  9:         ]]> 10:     </mx:Script> 11: 12:     <local:MovieLoader id="loader" movieLoadedEvent="movieLoadedListener(event)"/> 13: </mx:Application>

这里还有其它与元数据相关的有趣事情。如果你在编译器上设置一个标志(keep-as3-metadata 后面跟随一个标签的名称),则你可以添加你的自定义元数据标签,并且编译器将会把这些标签放入字节代码(bytecode)中。之后,你可以在运行时使用这些标签。例如,点击这里,你可以阅读到如何使用自定义元数据标签为在一个AIR应用程序中存储一个给定数据模型提供一种方法(稍后将更多地讨论Adobe AIR )。

现在,我们将讨论本节的最后一个话题:反射。为了能够使用自定义元数据,你必须依靠AS3反射API。

在PHP中,存在一个完整的面向对象的反射API系列:其中包括Reflection、ReflectionFunction、ReflectionParameter、ReflectionMethod等等。下面是一个在简单PHP类之上使用Reflection类的范例:

  1: class SimpleClass {  2:  3:     public $public = 'Public';  4:     protected $protected = 'Protected';  5:     private $private = 'Private';  6:  7:     private function SimpleClass() {  8:         echo('SimpleClass() called');  9:     } 10: 11:     private function __construct() { 12: 13:     } 14: 15:     function displayVar() { 16:         echo "SimpleClass class\n"; 17:     } 18: } 19: 20: 21: Reflection::export(new ReflectionClass('SimpleClass')); //this outputs: 22: 23: Class [ <user> class SimpleClass ] { 24:   @@ /Applications/MAMP/htdocs/_learning/classes.php 7-26 25: 26:   - Constants [0] { 27:   } 28: 29:   - Static properties [0] { 30:   } 31: 32:   - Static methods [0] { 33:   } 34: 35:   - Properties [3] { 36:     Property [ <default> public $public ] 37:     Property [ <default> protected $protected ] 38:     Property [ <default> private $private ] 39:   } 40: 41:   - Methods [3] { 42:     Method [ <user> private method SimpleClass ] { 43:       @@ /Applications/MAMP/htdocs/_learning/classes.php 13 - 15 44:     } 45: 46:     Method [ <user, ctor> private method __construct ] { 47:       @@ /Applications/MAMP/htdocs/_learning/classes.php 17 - 19 48:     } 49: 50:     Method [ <user> public method displayVar ] { 51:       @@ /Applications/MAMP/htdocs/_learning/classes.php 22 - 25 52:     } 53:   } 54: } 55:

在AS3中,可以用于反射的flash.utils 类包具有3个函数:describeType()、getDefintionByNameI() 和getQualifiedSuperClassName()。下面是一个describeType() 输出的范例(该输出是一个XML对象):

  1: public class SimpleClass {  2:  3:    public var _public:String = "Public";  4:    protected var _protected:String = "Protected";  5:    private var _private:String = "Private";  6:  7:    function SimpleClass() {  8:        trace("SimpleClass() called");  9:    } 10: 11:    public function displayVar():void 12:    { 13:        trace("SimpleClass class"); 14:    } 15: } 16: 17: function reflect():void { 18:     var s:SimpleClass = new SimpleClass(); 19:     var description:XML = describeType(s); 20:     trace(description); 21: } 22: 23: //the output: 24: <type name="org.corlan::SimpleClass" base="Object" isDynamic="false" isFinal="false" isStatic="false"> 25:   <extendsClass type="Object"/> 26:   <method name="displayVar" declaredBy="org.corlan::SimpleClass" returnType="void"/> 27:   <variable name="_public" type="String"/> 28: </type>

我的数据在哪里?将它显示出来!

作为一个PHP开发人员,你可以使用非常直接的方法读取数据、解析数据并且将这些数据在屏幕上显示出来。连接MySQL数据库是每一个PHP开发人员需要学会的第一件事情。实际上,我非常怀疑你能够一直阅读通篇文章至此而没有先偷看这一章节。

Flex怎么样呢?我不得不令你失望,因为你没有通向存储于数据库中的数据直接入口。不过,我猜想这其中肯定有些好事,因为你可以继续编写PHP文件来读/写数据库中的数据,甚至在编写Flex应用程序时也是如此 。为什么没有从数据库中读取数据的直接方法?因为有句老话说得好:“你应该绝不相信客户端!”

假设客户端是一个知道如何连接MySQL服务器的Flex组件。你如何存储其信任状(credential)才能使得其不容易被盗并且数据库的安全不受损害?为每个用户设置一个不同的用户密码然后向他们发送该信息?这就是为什么使用客户端技术直接连接数据库服务器,而不通过在中间使用应用程序服务器不是一个好主意的原因之一。

通常,在Flex应用程序中,你可以利用服务器侧的脚本来管理数据库。Flex可以为你提供一种调用服务器页面并且在Flex中返回相应的响应的方法。共有3种不同的连接服务器数据源的方式:REST 式样服务、 web 服务和 Remoting (或 RPC)。

你可以利用HTTPService 类来使用 REST 式样服务。当你需要发起一个请求时,你可以发送POST变量,并且其响应可以是XML、JSON (这里有一个用于解析JSON的第三方库)或自定义格式。

如果在服务器(SOAP/WSDL)上具有Web服务,你可以使用WebService类。

但最有趣的方法是remoting (使用 RemoteObject 类)。为什么我认为它是最酷的方法,这里有3个原因:首先,通过使用remoting,你可以通过调用任一public方法在你的服务器上充分利用你拥有的PHP类。通常,在Flex侧,你可以使用RemoteObject 的实例,好像其就是远端PHP类。其次,你可以将数据模型从PHP侧映射到ActionScript 数据模型,并且使得其自动完成转换。这是极为重要的,因为当你使用具有类型的对象时,你能够获得编译时间错误检查和代码完成功能的益处。这意味着代码更易于阅读并且不易出现软件缺陷。第三,该方法的消息格式-AMF3 (Action Message Format) 是二进制格式,该格式与SOAP/XML/JSON相比运行速度更快而且占用空间更小,特别地,对于具有大量数据的应用程序更是如此。该格式本身是开放的,而且所有的开发人员均可以阅读相关的白皮书并且能够实现使用该格式的程序。

AMF3之所以运行速度较快,是因为它能够对数据进行编码。因为,如果在一个数据集合中具有重复的相同字符串,则该字符串只需进行一次编码,而所有其它字符串的出现均为引用。如果一个数字少于4个比特,则只使用要求的最少数量的字节。

Adobe的James Ward 已经创建一种很好的测试方法,该测试方法能够说明remoting和其它方法之间的不同之处。

Flex在本机能够支持Remoting,但在服务器侧情况却是不同的。PHP不能在本机支持remoting 和AMF3。这是为什么你需要一个服务器侧库来支持PHP Web服务器的remoting 功能。共有4个可用的库,并且全部能够免费使用。我已经编写了关于如何使用这些库的指南:Zend AMF, PHPAMF, WebORB for PHP, SabreAMF。点击这里,你可以阅读一篇对它们进行比较的文章。

因为本机数据类型能够自动转换(PHP类型至AS3类型的转换以及相反方向的转换),因此你必须关注来自一种语言的本机类型是如何转换为其它类型的。这里 是一个AMFPHP 库中的数据类型一致性的范例。

Flex和PHP 项目的用户授权

在Flex和PHP项目中用户授权是如何进行的呢?答案非常简单,与PHP Web站点相同,你可以使用会话和某种方法来验证用户密码即可。

通常,当在Flex中执行一个调用时,会话ID将会被自动挂起。因此,如果之前已经对用户进行授权,则将使用相同的会话。

点击这里,你将会获得关于该话题的更多信息。

建立Flex和PHP 项目

令人欣慰的是,PHP和Flex均为成熟技术,因此当需要使用工具时,你可以具有大量的选择。我将在本章节中讨论其中一些工具。

Flex SDK和文本编辑器

你可能考虑的第一个选择是使用免费的开源Flex SDK,特别地,如果你非常喜欢命令行工具和文本编辑器(例如vi )。你可以使用你喜欢的文本编辑器编写代码,以及使用命令行工具来编译/调试应用程序。

Flex Builder / Flash Builder 和 Eclipse PDT / Zend Studio

我喜欢使用一种现代IDE。对于Flex 和 PHP 项目来说,最佳的组合可能是Flex Builder 或Flash Builder 4 与Zend Studio之间的组合。Flex Builder 是 Adobe的 Flex IDE直到第四版 的名称;第四版被重新命名为Flash Builder 4。该IDE基于Eclipse,它支持Windows 和Mac OS,并且它是以插件版本和独立版本的方式出现。例如,你具有Zend Studio,你可能考虑使用 Flash Builder 插件,然后在Zend Studio之上安装该插件。 你可以使用 Flex Builder 3 60 天 (试用版本),并且现在(2009年夏)的 Flash Builder 4 为 beta 版本。如果你是教师或学生,则可以免费获得一个许可。

如果你喜欢使用Eclipse PDT,则可以使用相同的方法:安装Flash Builder 4 的插件版本,或从相反方向,在Flash Builder 独立版本之上安装PDT。

Flash Builder 4能够提供使用PHP 和Flex的向导:它能够内视PHP代码并且产生AS3和Flex代码(这里 是关于该话题的指南)。你可以使用它调试、配置、编译以及启动应用程序(Web 或 AIR)。你也可以导出应用程序以便进行发布,另外,Flash Builder 4 还支持重构,以及一个Design 视图和一个状态编辑器。

Flash Builder 4 还能够与Flash Catalyst进行集成,这样,你可以在Flash Catalyst中创建应用程序的UI,然后在Flash Builder 4 中打开创建的项目并且继续添加业务逻辑(你可以点击屏幕截取来看一看如何使用 Adobe Illustrator、Flash Catalyst和 Flash Builder 4创建垂直滚动条)。

Flex支持的其它IDE (商用产品) 包括 IntelliJ IDEA 和FDT (基于Eclipse )。

调试Flex应用程序

你可以使用Flex SDK的调试器或Flash Builder 4 (或 Flex Builder 3)的调试器来调试Flex代码。如果你选择 Flash Builder 4 和Zend Studio的组合配置,则你可以非常方便地在相同的项目中调试PHP 和Flex 代码。你可以在执行一个从Flex至PHP的调用之后进入PHP调试器,然后当Flex收到响应时,你就可以进入Flex调试器。点击这里1 和 这里2 可以看到关于该话题的一些视频,点击 这里3 可以下载关于Zend Studio 和Flex Builder 调试的指南。

在PHP中,当我遇到软件缺陷时,我首先采用的方法是使用die() 和var_dump()的调用组合来查看究竟发生了什么问题。在AS3中,你可以使用 trace() 向显示终端输出变量值。一个很酷的功能是当你编译用于生产的应用程序时,所有的trace() 语句均被删除。这是一个无阻塞输出消息的方式。你也可以使用Alert 类在弹出视窗中显示消息 (更像 在进行FireBug之前的JavaScript 调试 )。

你需要时刻牢记的关键之处是:现在你已经具有了独立于服务器的客户端,并且问题可能存在于客户端、服务器或网络层面。

你可以点击这里阅读关于调试Flex 和PHP的更多内容。

什么是Adobe AIR

Adobe AIR 是 RIA的一个桌面运行时,而RIA能够作为桌面应用程序运行于Windows、Mac和 Linux。利用AIR,你可以创建能够运行于上述任一操作系统的单一应用程序。AIR应用程序的范例包括:Tour de Flex、TweetDeck、Times Reader、Dojo Toolbox 和 Sideline from Yahoo!。

你可能将Adobe AIR 看作一个桌面 “Flash Player” 。然而,Adobe AIR 绝不仅仅是一个修改的Flash Player。

在该运行时内部,具有一个 HTML 引擎(WebKit, 与 Safari 和Google Chrome使用的引擎相同) 和一个修改的Flash Player引擎。这两个引擎提供了一个API集合,该API集合提供了访问AIR应用程序运行的设备的通路。这些API可以用于读写磁盘文件、检测网络连接性、检测连接显示器的数量和分辨率、应用程序更新、桌面通知、本地数据库以及拖拽等等。

作为一个Web开发人员,你可以选择下列技术的任意组合:Flex、ActionScript 3或 HTML/CSS/JavaScript。你可以只使用HTML、JavaScript和 CSS即可创建AIR应用程序。实际上,Dojo Toolbox 和 Yahoo!的Sideline 就是使用HTML/CSS/JS创建的。

因此利用AIR,你可以充分利用你的现有技术来创建桌面应用程序。但是,为什么你希望创建作为桌面应用程序运行的Web应用程序呢?实际上,这里存在的原因很多:

  • 在你没有因特网连接的情形下,你希望能够使用应用程序或部分应用程序。
  • 你希望摆脱browser chrome的控制,并且完全自定义应用程序的外观。
  • 你希望能够在用户的计算机中将应用程序与其它应用程序集成(例如,将文件从AIR app拖拽至桌面,反之亦然)。
  • 你希望能够在用户的设备上存储文件。
  • 你希望建立一个通知系统,并且希望在系统盘中以最小化的方式运行应用程序(例如,及时消息服务可以在收到一个新的消息时通知你,尽管它们处于最小化状态并且不在注视范围之内)。

对于开发AIR应用程序来说,你可以使用免费的AIR SDK (你可以获得用于创建、测试和调试的命令行工具),你可以使用 Aptana Studio (如果你希望使用 HTML/JS/CSS创建AIR应用程序),以及使用 Flash Builder 4 (或 Flex Builder 3)。

最后需要说明的是,为浏览器创建的任何Flex应用程序可以立即转换为AIR应用程序。当然,如果你停止在这里并且不希望充分利用特定的AIR功能,则上述转换毫无意义,因为你没有提供任何附加的价值。

下一步计划是什么?

明年初Flex 4将会面世。Adobe 已经开发了一个名称为 Flash Catalyst 的新工具(目前仍处于beta版状态),该工具可以用于将在Photoshop 或Illustrator 中创建的静态设计转换Flex的功能用户界面。设想一个工具可以用于输入Photoshop 或Illustrator文件、输出HTML/CSS/JavaScript代码以及保持输入文件的外观。这就是Flash Catalyst目前能够完成的任务,只不过它输出的是Flex 4代码而不是HTML代码。

同时,我们致力于使得Flash Platform 能够运行于所有的屏幕:从计算机到移动设备,以及从机顶盒(set-top box)到电视机。目前,Flash Lite 3 可以用于各种手机(诺基亚、索尼爱立信、HTC、Android和Palm)。明年,我们将推出支持手机的Flash Player 10版本。另外,明年很可能我们可以看到支持Flash的电视机。如果用户界面能够充分利用所有的Flash Player 功能,或者能够从Web观看高清视频(Flash 能够支持 H-264 标准),那将是多么有趣的事情。一些分析专家认为在不久的将来,连接因特网的手机数量将超过计算机数量,并且大多数人在身边没有计算机时将会使用手机作为访问因特网的主要手段。

对于Web开发人员来说这意味着什么?这意味着你可以扩展你的专业技术领域和你从Web站点提供给桌面RIA(使用Adobe AIR),以及从计算机提供给手机和其它具有屏幕的设备的功能。当然,为了实现上述目标,你需要提升你在某些方面的技能,但对于给定的平台,其复杂程度还不及成为一个高效C或C++ 开发人员所需的技能。

总结

我希望你已经找到了一些回答你的问题的答案。如果你对进入Flex世界是认真的,下面是一些资源:

Flex之旅

我将Tour de Flex 视为可爱的php_manual.chm的Web 2.0 或RIA版本。你可以从这里安装Tour de Flex。Tour de Flex提供了如何在Flex中使用组件的范例(你可以看到组件外观以及用于实现组件的代码)。你也可以方便地访问相应的Flex文档。该应用程序本身是使用Flex 和Adobe AIR创建的。

书籍

目前关于Flex 和ActionScript 3的书籍很多。但是,我个人喜爱并且我经常推荐的书籍如下所列:

  1. Flex入门(First steps in Flex),作者是 Bruce Eckel 和James Ward(Adobe的技术作者);该书给出了如何使用Flex开发Web和桌面应用程序的概述,并且你可以在一个周末读完全书。
  2. ActionScript 3精华读本(Essential ActionScript 3) ,作者是Colin Moock;这是一本厚书,如果你能够在一个周末读完全书,那么你将是一个卓越的阅读家。尽管本书没有涵盖Flex内容,但你可以学到几乎所有关于ActionScript 3的知识。

Web站点

目前,涉及Flex 和ActionScript 3的Web站点和博客数以百计。因此,不可能为你提供完整的站点列表,但我将给出我所喜爱的站点:

  • Adobe 开发人员中心(Adobe Developer Connection)。一个阅读Flash、Flex和 AIR技术文章的极佳去处。该Web站点每周定期更新。 其中有个 章节 专门讨论 Flex和 PHP。
  • Adobe 电视(Adobe TV)。该站点为你提供会议演讲的巨量视频信息以及视频资料。
  • Gotoandlearn.com。 一个优秀的视频资料集,由会员作者Lee Brimelow编写。
  • Flex 文档(Flex documentation)。记住你可以下载该文档的ZIP文件。你可以点击这里 以便查看多种格式的资源。
  • Adobe 社区组(Adobe Groups)。一个免费平台,在此你可以寻找Adobe用户组(包含Flex 或Flash 组)。浏览一下该平台,也许可以发现与你的领域相关的Flex 用户组,并且他们通常每个月组织一次会议。该平台拥有多个论坛,并且支持本地化,因此,平台内容不仅限于使用英语。
  • 我的博客链接(My blogroll)。查看一下该博客链接,其中大部分博客主人是来自Adobe的,他们撰写与Flash 和 Flex相关的博文。
  • 请访问我自己维护的关于框架、库和其它Flex相关资源的列表。

现在,我认为,你可以理解为什么当一个开发人员问我什么是Flex时,我会面无表情地凝视片刻。在这片刻时间,所有我在这里撰写的千言万语浮现在我的眼前…我想讨论的东西很多,无法简单地用只言片语来表达呢!希望现在我能够这样回答你:“老兄,Flex是令人敬畏的!看看本文吧 ”。

如果你有任何评论,请抽出宝贵时间在本页留下,本人不胜感激!

本页面已经被浏览15,365次。