图五十五
组建与控件的关系

Adobe Flex 支持组件开发模型,通常情况下,我们使用 Flex 中预定义的组件,根据需求开发应用程序。当预定义组件不能满足需求时,我们还可以创建自定义组件来构建应用程序。在软件行业中,一般把“component”翻译为“组件”,把“control”翻译为“控件”。其中,术语“组件”指任何可复用的、可以与其他对象交互的对象,如 Flex 中的 validators、formatters、effects、managers、controls、containers 等等;术语“控件”则指能够在界面上看到的组件,也称为可视组件,如 Flex 中的 controls、containers、borders 等等。所有的控件都是组件,反之,则不一定。Flex 中的所有类都可以被称为组件,其中能够在界面上看到的组件都可以被称为控件。
为什么要创建自定义组件
- 通过组件,我们可以将应用程序分为能够独立开发和维护的模块。通过在自定义组件中实现通用逻辑,我们可以创建一系列可重用的组件,实现多个应用程序间的代码共用。
- 此外,通过让自定义组件继承 Flex 中预定义的类,我们可以扩展 Flex 中的类,给现有的组件添加更多的行为,或者实现具有全新行为的组件。比如,我们可以创建自定义版本的 Flex 可视组件及非可视组件。
我们可以通过在一个单独的 MXML 文件中包含所有 MXML 代码和支持性的 ActionScript 代码来构建整个应用程序。随着应用程序不断变大,这个 MXML 文件的大小和复杂度不断增长,应用程序很快会变得难以理解和调试,多个开发人员同时的话将十分困难。为了解决这个问题,我们可以将整个应用程序分为互不关联的功能单元,也称为模块。从而,不同的开发人员或者开发小组可以独立的开发和调试各个模块;可以在不同的应用程序中使用开发好的模块,从而避免重复性的工作;可以更快的分离、调试应用程序中产生的错误。
在 Flex 中,一个模块对应着一个在 MXML 文件或者 ActionScript 文件中实现的组件。下图显示了如何将一个应用程序分为多个组件。 (图五十六)
图五十六
应用程序分为多个组件

怎样创建自定义组件
Flex 是以多个 ActionScript 类层次结构的形式来实现的。这些类层次结构中定义了组件类(各种控件)、管理类(Manager 类)、数据服务类(HttpService、WebService 等)以及实现 Flex 其他特性的类。通过继承类层次结构中相应的类,我们可以扩展现有的组件或者创建新的组件来实现自定义组件。
在 Flex 中,所有的可视组件都继承自 UIComponent ActionScript 类。常用的非可视组件有 Validator、Formatter 和 Effect。我们通过使用 MXML 和 ActionScript 语言扩展类层次结构来创建自定义组件。这些组件将继承超类中的属性、方法、事件、样式和特效。 (组图五十七)
图五十七(组)
常用的非可视组件
UIComponent 类层次结构

Validator 类层次结构

Formatter 类层次结构

Effect 类层次结构

通过扩展现有的组件来创建自定义组件:
- 我们可以定制现有的组件,使其满足应用程序的要求。比如,我们可以创建一个继承自 Button 的自定义组件,将其“Label”属性设定为“提交”,用来统一所有表单上的提交按钮。
- 我们可以修改现有组件的行为。比如,让所有 VBox 中的组件都按从下至上的顺序排列。
- 为现有的组件添加新的逻辑和行为。比如,我们可以让 TextInput 控件支持组合按键来删除输入的内容。
当扩展现有的组件无法满足应用程序要求时,我们需要创建新的自定义组件。在 Flex 中,如果创建的自定义组件是可视组件,并希望能够被 addChild 及相关方法所调用(这意味着所创建的自定义组件可以添加到容器中去,可以参与界面的布局),所创建的自定义组件需继承 UIComponent 类。
在实现自定义组件之前,我们需要决定是在 MXML 文件中实现还是在 ActionScript 文件中实现,这取决于应用程序的需求。
- MXML 组件和 ActionScript 组件两者都定义新的 ActionScript 类。
- 基本上所有在 ActionScript 组件中能做的事情都可以在 MXML 组件中做。对于简单的组件,比如修改现有组件的行为或者为其添加新的基本特性,在 MXML 中实现起来更快、更简单。
- 如果组件是一个包含其他组件的组合组件,而且需要用布局容器来设置这些组件的大小和位置,使用 MXML 来定义组件。
- 如果需要修改组件的行为,例如对子组件进行布局,应当使用 ActionScript。
- 如果是通过继承 UIComponent 来创建一个可视组件,使用 ActionScript。
- 如果是创建非可视组件,例如 fomatter、validator 或者 effect,使用 ActionScript。
- 如果是添加日志支持,使用 ActionScript。
用 ActionScript 创建自定义组件时,需要创建一个继承自 Flex 类层次结构中相关类的子类。这个子类的名称(例如 MyAsButton),必须同 ActionScript 文件相同(比如,MyAsButton.as)。子类将继承超类中的所有属性和方法。该例中,我们可以通过<MyAsButton> 标签在 MXML 中引用这个组件。
当我们使用 MXML 来定义组件时,Flex 编译器自动创建一个 ActionScript 类,MXML 文件的名称(比如,MyMXMLButton.mxml)对应自动创建的 ActionScript 类的名称。该例中,自动生成的 ActionScript 类名称为 MyMXMLButton,我们可以通过<MyMXMLButton> 标签在 MXML 中引用这个组件。
下图显示了两个继承自 Flex Button 组件的组件,一个定义在 ActionScript 中,一个定义在 MXML中。 (图五十八)
图五十八
应用程序分为多个组件

两个组件都继承自 Button 类。因此,两个组件都继承了 Button 类中标记为 public 和 protected 的属性、方法及其他元素。在任意一个组件中我们都可以重写继承的元素、定义新的元素以及添加自定义的逻辑。
我们无法重写由变量定义的属性,但是可以重写由 getter 和 setter 方法定义的属性。 我们可以重新设置该属性的值。通常情况下,我们可以在 ActionScript 组件的构造函数重新设置该属性的值; 因为 MXML 组件不支持构造函数,因此我们需要在事件处理程序中设置该属性的值。
此外,当我们使用 MXML 自定义组件的时候,Flex 编译器将帮助我们完成大量的工作,例如调用 addChild 将子组件添加到自定义组件中。这使得在 MXML 中创建自定义组件比在 ActionScript 容易的多。
如何在项目中使用自定义组件
创建好的自定义组件以特定类型的文件形式存在于文件系统中,我们需将这些文件引入我们的项目,然后才可以在项目中使用这些组件。
| 文件类型 | 扩展名 | 说明 |
|---|---|---|
| MXML | .mxml | 在 MXML 文件中实现的组件。 |
| ActionScript | .as | 在 ActionScript 类中实现的组件。 |
| SWC | .swc | 在 MXML 或 ActionScript 文件中实现组件后,将其打包进 SWC 文件中。SWC 文件中包含打包好的可以在多个应用程序间复用的组件。当生成 SWF 文件时,SWC 文件会被编译进应用程序中。 |
| RSL | .swc | 在 MXML 或 ActionScript 文件中实现组件后,可以通过 RSL 文件来部署。RSL 是一种独立文件,能够被 SWF 文件分别下载并缓存在客户端供多个应用程序的 SWF 文件使用。 |
对于 MXML 和 ActionScript 文件
当使用 Flex SDK 编译应用程序的时候,我们需要设置定义了自定义组件的 MXML 或者 ActionScript 文件是位于应用程序的目录结构中,还是位于由多个应用程序共享的目录结构中。
例如,当我们创建只有一个应用程序使用的组件的时候,通常情况下将组件存放在应用程序的目录结构中。我们可以在应用程序主文件所在的目录下创建子目录,然后将定义了组件的 MXML 或者 ActionScript 文件放置在创建的子目录中。当 Flex 编译应用程序的时候,组件将随同整个程序被编译进产生 SWF 文件中。
或者,当组件由多个应用程序共享的时候,我们将定义了组件的 MXML 或者 ActionScript 文件放在一个共享的目录结构中,并将其包含在应用程序的 ActionScript 源路径中。当 Flex 编译应用程序的时候,也会编译包含进 ActionScript 源路径的组件。
编译时,通过下列方法设置共享组件所在的目录:
- 对于 Flash Builder,打开项目属性对话框,然后选择构建路径设置 ActionScript 源路径。
- 对于 mxmlc 编译器,设置 source-path 选项来设定包含共享的 MXML 和 ActionScript 文件的目录的位置。
对于 SWC 文件
SWC 文件是 Flex 组件的存档文件。SWC 文件使得开发者之间能够很容易的交换组件。只需要交换一个单独的文件便可以了,而不是一大堆的 MXML 或者 ActionScript 文件、图片或其他资源文件。此外,SWC 文件中的 SWF 文件是编译过的,这意味着其中的代码在通常情况下是隐藏了的。最后,将组件编译为 SWC 文件能够很容易的实现名字空间的分配。
SWC 文件包含一个或多个组件,并且按 PKZIP 格式压缩或解压缩。我们可以用 WinZip、JAR 或者其他存档工具打开。然而,不要手动改变 SWC 文件中的内容,不要尝试在外部运行的 SWC 文件中包含的 SWF 文件。
编译时,通过下列方法设置 SWC 文件所在的目录:
- 对于 Flash Builder,打开项目属性对话框,然后选择构建路径设置包含 SWC 文件的库路径。
- 对于 mxmlc 编译器,设置 library-classpath 选项来设定包含 SWC 文件的目录的位置。
对于 RSL 文件 一种减少应用程序的 SWF 文件的方法便是将共享的资源放置在独立的文件中。这些独立的文件可以被分别下载并缓存在客户端。这些共享的资源只需要在客户端下载一次,就能够被多个应用程序在运行时加载。这些共享的文件被成为运行时共享库(RSLs)。
如果我们有多个应用程序, 并且这些应用程序共享一些列核心的组件和类,我们的用户只需要以 RSL 形式下载这些共享资源一次。只要应用程序在同一个域下,那么这些应用程序将使用同一个缓存过的 RSL。通过使用 RSL,我们能够减少最终生成的 SWF 文件的体积。使用 RSL 的最终效果,取决于使用该 RSL 的应用程序的数量。如果只有一个应用程序使用该 RSL,那么不会减少下载的总量,反而可能增加下载量。
编译时,通过下列方法设置 RSL 文件所在的目录:
- 对于 Flash Builder,打开项目属性对话框,然后选择构建路径设置包含 SWC 文件的库路径。
- 对于 mxmlc 编译器,设置 external-library-path 选项来设定编译时 RSL 文件的路径。设置 runtime-shared-libraries 来设定应用程序发布时 RSL 文件的相对位置。
总结:
本节主要介绍了为什么自定义组件以及如何自定义组件。为了实现模块化编程及代码的重用,我们通过继承 Flex 类层次结构中相关的类,在 MXML 文件或者 ActionScript 文件中实现自定义组件,并通过 MXML 文件、ActionScript 文件、SWC 文件将其引入我们的应用程序中。为了让大家分清楚组件和控件的区别,本节还简单提及了组件和控件的概念。 此外我们可以看到,如果一个组件是定义在MXML文件中的,我们可以称其为 MXML 组件;如果是定义在 ActionScript 文件中的,我们则称其为 ActionScript 组件。接下来的内容中,我们将通过两个例子,来看看具体是如何实现自定义组件的。
思考:
编译使用 MXML 定义的组件的时候,如何知道 Flex 编译器自动创建了哪些代码?