第二篇 FLEX核心开发技术
在第一篇中我们了解了当前RIA世界的现状,后半部分也就是第二章,宏观地介绍了FLEX相关一些很重要也很基础的概念。本篇将如题所述深入详细地探讨FLEX编程的方方面面,以及作者的经验总结。本篇这也是本书的重点。
第三章 FLEX编程语言
不知道你注意到没有,在我们应用FLEX开发的过程中,构建表现层布局,MXML编程和ACTIONSCRIPT基本上都可以满足我们的需求,那么MXML和ACTIONSCRIPT分别的侧重点又是什么呢,或者换一种表达方式,MXML和ACTIONSCRIPT分别的长处和短处又是什么呢。为了分析这个问题,我们引用在企业级开发中的另一种相似的情景,尤其是对J2EE熟悉的开发者来说,就是JSP和SERVLET的角色和使命。
JSP从本质上来说是SERVLET的另一种表达方式,都是以单例的形式多线程的响应客户端请求。从编译角度来说,JSP首先会被转换成SERVLET的形式,以CLASS字节码文件等待JAVA虚拟机JVM的最终解析和执行。JSP更擅长从最终用户的角度构建系统的表现层,而传统的SERVLET则更擅长业务逻辑的处理。反观我们的MXML和ACTIONSCRIPT,其所处的角色和相对的关系与JSP和SERVLET有着及其的相似性。MXML以标签的方式来布局最终用户的表现层,MXML类实际上是ACTIONSCRIPT类的另一种表现方式,之所以会有这种表现方式,是因为它能够及其方便开发者快速的构建用户的表现层,而没有必要在ACTIONSCRIPT脚本中以硬编程的方式来实现布局,尤其是复杂的页面布局。编译期,MXML也会以ACTIONSCRIPT文件为中间形态,最后形成SWF字节码供AVM虚拟机解析和执行。
MXML可以快速的构建表现层页面是其优点之一,另外还有一点,就是MXML本身的数据绑定机制的实现。这一点可以让我们很好地封装和隔离页面代码和逻辑代码,为和服务器端的集成提供了很基础的保证。
3.1 利用容器类布局表现层
MXML标准实现包括了两种类型的组件,控件和容器。控件一般指TextInput, Label,Button等实体页面元素。相应地,容器一般指一块长方形的空间,在这个空间里可以按照当前这个容器预定义的放置规则放置控件和别的容器。在种类上容器分布局性容器和导航性容器两种。
上面我们提到容器的放置规则,那么什么是放置规则呢?我们现在讨论这个话题。举个例子,对于中国象棋和国际象棋来说,棋子的摆放和挪动规则是不一样的,我们不能在中国象棋的棋盘上玩国际象棋。MXML容器内页面元素的放置规则可从总体上分为以下几种。
一.横向或纵向规则。
二.网格式规则。
三.坐标式规则。
四.点缀式规则。
五.复用式规则。
下面我们将举例分析以上四种方式。
3.1.1 横向纵向规则
在FLEX所有的标准容器内以字母H或V打头的容器组件基本都属于这种规则,如Hbox,/Vbox,HdevidedBox/VdevidedBox, 它们在本质上是近亲,有着共同的祖先Box,参考图3-1-1-1,
在Box容器中有个direction属性,可以设其为horizontal或者vertical,在功能上和Vbox和Hbox一摸一样。实际上,Vbox和Hbox就是direction属性已经初始化为horizontal或者vertical的Box,参考下面两个例子实际上是等同的。
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Box id="TEST1" direction="horizontal" label="horizontal box">
</mx:Box>
<mx:Box id="TEST2" direction="vertical" label="vertical box">
</mx:Box>
</mx:Application>
以及
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:HBox id="TEST1" label="horizontal box">
</mx:HBox>
<mx:VBox id="TEST2" label="vertical box">
</mx:VBox>
</mx:Application>
对于Hbox/Vbox的基本用法,参考示例:
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Panel height="75%" width="75%"
paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10">
<mx:HBox borderStyle="solid" paddingTop="10" paddingBottom="10"
paddingLeft="10" paddingRight="10">
<mx:Button label="Button 1"/>
<mx:Button label="Button 2"/>
<mx:Button label="Button 3"/>
<mx:ComboBox/>
</mx:HBox>
<mx:VBox borderStyle="solid" paddingTop="10" paddingBottom="10"
paddingLeft="10" paddingRight="10">
<mx:Button label="Button 1"/>
<mx:Button label="Button 2"/>
<mx:Button label="Button 3"/>
<mx:ComboBox/>
</mx:VBox>
</mx:Panel>
</mx:Application>
运行结果:
在实现上HdividedBox/VdividedBox分别是direction属性已经被预设为horizontal和vertical的DividedBox。DividedBox比其父亲Box多了一个可以通过拖动重新设定容器大小的属性。
参考实例代码
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
backgroundColor="white" width="531" height="312">
<mx:HDividedBox width="100%" height="100%">
<mx:Panel title="zone 1" width="30%" height="100%">
</mx:Panel>
<mx:VDividedBox width="70%" height="100%">
<mx:Panel title="zone 2" width="100%" height="50%">
</mx:Panel>
<mx:Panel title="zone 3" width="100%" height="50%">
</mx:Panel>
</mx:VDividedBox>
</mx:HDividedBox>
</mx:Application>
运行结果:
3.1.2网格式规则
在FLEX标准组件库中利用网格式布局孩子元素的主要有Grid和Tile两个。所谓网格式布局就是指在一个长方形的容器空间内按照预设的行数和列数对空间进行打格子划分,孩子元素可被放到相应的格子中去。Grid容器利用配套的GridRow和GridItem元素来设置网格布局。参考示例
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Grid id="grid">
<mx:GridRow id="row1" >
<mx:GridItem>
<mx:Button label="Button 1"/>
</mx:GridItem>
<mx:GridItem>
<mx:Button label="Button 2"/>
</mx:GridItem>
<mx:GridItem>
<mx:Button label="Button 3"/>
</mx:GridItem>
</mx:GridRow>
<mx:GridRow id="row2">
<mx:GridItem colSpan="3" horizontalAlign="center">
<mx:Button label="Button 4"/>
</mx:GridItem>
</mx:GridRow>
<mx:GridRow id="row3">
<mx:GridItem/>
<mx:GridItem colSpan="2" horizontalAlign="center">
<mx:Button label="Button 5"/>
</mx:GridItem>
</mx:GridRow>
</mx:Grid>
</mx:Application>
运行结果:
Tile则默认以最大的孩子组件的大小为标准,以相同的高度和宽度来布局它的孩子组件。也可以通过cellWidth和cellHeight属性设定孩子元素的大小。参考示例:
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal">
<mx:Panel height="291" width="243"
paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10">
<mx:Tile direction="vertical" borderStyle="inset"
horizontalGap="10" verticalGap="15"
paddingTop="10" paddingBottom="10" paddingLeft="10" paddingRight="10">
<mx:Button label="1" height="50" width="75"/>
<mx:Button label="2" height="50" width="75"/>
<mx:Button label="3" height="50" width="75"/>
<mx:Button label="4" height="50" width="75"/>
<mx:Button label="5" height="50" width="75"/>
<mx:Button label="6" height="50" width="75"/>
</mx:Tile>
</mx:Panel>
<mx:Panel height="293" width="321"
paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10">
<mx:Tile direction="horizontal" borderStyle="inset"
horizontalGap="10" verticalGap="15"
paddingTop="10" paddingBottom="10" paddingLeft="10" paddingRight="10">
<mx:Button label="1" height="50" width="75"/>
<mx:Button label="2" height="50" width="75"/>
<mx:Button label="3" height="50" width="75"/>
<mx:Button label="4" height="50" width="75"/>
<mx:Button label="5" height="50" width="75"/>
<mx:Button label="6" height="50" width="75"/>
</mx:Tile>
</mx:Panel>
</mx:Application>
运行结果:
3.1.3 坐标式规则
在FLEX标准容器组件中采用显示绝对坐标布局孩子元素的容器是Canvas组件,它通过孩子元素的x和y属性来设定其在Canvas长方形空间内的位置。参考示例:
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Panel height="255" width="311" paddingTop="5" paddingLeft="5" paddingRight="5" paddingBottom="5">
<mx:Canvas borderStyle="solid" height="200" width="261">
<mx:VBox right="115" bottom="50" width="75" height="75" backgroundColor="#0080C0"/>
<mx:VBox right="70" bottom="30" width="75" height="75" backgroundColor="#FFFF80"/>
<mx:VBox right="25" bottom="10" width="75" height="75" backgroundColor="#8080C0" alpha="0.8"/>
</mx:Canvas>
</mx:Panel>
</mx:Application>
运行结果:
3.1.4点缀式规则
一. 所谓点缀式规则,实际上是指当前容器参与同级容器的布局中,只是起点缀的作用,其大小不会影响到它点缀对象的大小的一种容器,比如ControlBar和ApplicationControlBar,都一般和Panel容器或者TitleWindow容器一起出现扮演点缀的角色。ApplicationControlBar 通常被用来格式化页面布局的顶端,如顶部主菜单栏、顶部按钮等。它也通常和Panel或者TitleWindow一起使用。参考示例:
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
]]>
</mx:Script>
<mx:XMLList id="menu">
<menuitem label="File">
<menuitem label="New" data="New"/>
<menuitem label="Open" data="Open"/>
<menuitem label="Save" data="Save"/>
<menuitem label="Exit" data="Exit"/>
</menuitem>
<menuitem label="Edit">
<menuitem label="Cut" data="Cut"/>
<menuitem label="Copy" data="Copy"/>
<menuitem label="Paste" data="Paste"/>
</menuitem>
<menuitem label="View"/>
</mx:XMLList>
<mx:Array id="arr">
<mx:String>Item 1</mx:String>
<mx:String>Item 2</mx:String>
<mx:String>Item 3</mx:String>
</mx:Array>
<mx:ApplicationControlBar id="dockedBar" height="100%" dock="true">
<mx:MenuBar height="100%"
dataProvider="{menu}"
labelField="@label"
showRoot="true"/>
<mx:HBox paddingBottom="5"
paddingTop="5">
<mx:ComboBox dataProvider="{arr}"/>
<mx:Spacer width="100%"/>
<mx:TextInput id="myTI" text=""/>
<mx:Button id="srch1"
label="Search"
click="Alert.show('find')"/>
</mx:HBox>
</mx:ApplicationControlBar>
<mx:TextArea width="100%" height="100%"/>
</mx:Application>
注意到ApplicationControleBar组件的高度已经设为100%,同级组件TextArea的高度和宽度也设为100%,可是显示结果ApplicationControlBar的高度还是默认高度,并没有覆盖掉TextArea的空间。运行结果为
而相应地对于ControlBar容器,和ApplicationControlBar相似,只不过ControlBar用来格式页面底部的布局。它也通常和Panel或者TitleWindow一起使用。参考示例:
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
private function doit():void {
}
]]>
</mx:Script>
<mx:Panel title="CONTROLBAR DEMO"
paddingTop="10" paddingBottom="10"
paddingLeft="10" paddingRight="10">
<mx:HBox width="250" height="200">
</mx:HBox>
<mx:ControlBar width="250">
<mx:Label text="TEST"/>
<mx:NumericStepper/>
<mx:Spacer width="100%"/>
<mx:Button label="DO"
click="doit();"/>
</mx:ControlBar>
</mx:Panel>
</mx:Application>
运行结果:
3.1.5复用式规则
所谓复用规则是指,在某些容器的长方形空间内,所有的孩子元素中在某一时刻只有一个处于显示状态,其他的都处于隐藏状态。基本所有的导航式容器都遵循这种规则。导航类容器在功能上不具有布局类容器的特性,它基本上都是以分层的方式共享同一块容器空间(孩子元素的状态分为两种显示和隐藏),并通过设定其内置的selectedChild或者selectedIndex属性的方式在不同的层之间导航。为充分利用有限空间提供了很好的方案。导航类容器主要分为:Accordion,TabNavigator,ViewStack。
一. Accordion 包含一个子容器的集合,并且通过bar点击的方式设置内置的selectedChild或者selectedIndex属性在不同子容器间导航,示例代码:
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Accordion id="accord" height="234" width="526">
<mx:Form label="label1">
<mx:FormItem label="First Name">
<mx:TextInput />
</mx:FormItem>
</mx:Form>
<mx:Form label="label2">
</mx:Form>
<mx:Form label="label3">
</mx:Form>
<mx:Form label="label4">
</mx:Form>
</mx:Accordion>
</mx:Application>
运行结果为
二 TabNavigator容器示例代码:
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Panel height="90%" width="455" paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10">
<mx:TabNavigator id="tab" width="410" height="100%" >
<mx:VBox label="Panel 1">
<mx:Label text="panel 1"/>
</mx:VBox>
<mx:VBox label="Panel 2">
<mx:Label text="panel 2"/>
</mx:VBox>
<mx:VBox label="Panel 3">
<mx:Label text="panel 3"/>
</mx:VBox>
<mx:VBox label="Panel 4">
<mx:Label text="panel 4"/>
</mx:VBox>
</mx:TabNavigator>
<mx:HBox>
<mx:Button label="Button1" click="tab.selectedIndex=1"/>
</mx:HBox>
</mx:Panel>
</mx:Application>
运行结果:
三ViewStack容器没有显式的提供子容器间的导航方式默认它首先加载容器内第一个孩子元素也就是其selectedIndex默认为0,但是可以结合其他控件轻松的实现子容器的导航。示例代码。
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:VBox>
<mx:HBox borderStyle="solid">
<mx:Button id="button1"
label="button1"
click="myViewStack.selectedIndex=0"/>
<mx:Button id="button2"
label="button2"
click="myViewStack.selectedIndex=1;"/>
<mx:Button id="button3"
label="button3"
click="myViewStack.selectedIndex=2;"/>
</mx:HBox>
<mx:ViewStack id="myViewStack" borderStyle="solid" width="100%">
<mx:Canvas id="Q" label="Q">
<mx:Label text="TEST1"/>
</mx:Canvas>
<mx:VBox label="W">
<mx:Label text="W"/>
</mx:VBox>
<mx:VBox label="E">
<mx:Label text="E"/>
</mx:VBox>
</mx:ViewStack>
</mx:VBox>
</mx:Application>
运行结果:
3.1.6 其他
在FLEX的标准容器库内有三个特例,分别为Application, Panel和TitleWindow。说特殊是因为它们的布局方式默认为纵向布局。但是它们都有一个layout属性可以分别指定为vertical,horizontal和absolute.当layout属性设置为vertical时就像Vbox,当为horizontal时,布局方式就是一个Hbox,而当是absolute时,就变成了一个Canvas.就是说它们的布局方式包含纵向横向规则和坐标式规则。以Panel为例,下面的三个示例分别展示了三种不同的布局选择。
一 横向布局
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Panel title="LAYOUT Example" status="Active" height="185" width="537" layout="horizontal" >
<mx:Button label="BUTTON1" />
<mx:Button label="BUTTON2" />
<mx:Button label="BUTTON3" />
<mx:Button label="BUTTON4" />
<mx:Button label="BUTTON5" />
</mx:Panel>
</mx:Application>
运行结果:
二 纵向布局
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Panel title="LAYOUT Example" status="Active" height="185" width="537" layout="vertical" >
<mx:Button label="BUTTON1" />
<mx:Button label="BUTTON2" />
<mx:Button label="BUTTON3" />
<mx:Button label="BUTTON4" />
<mx:Button label="BUTTON5" />
</mx:Panel>
</mx:Application>
运行结果:
三 绝对坐标布局
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Panel title="LAYOUT Example" status="Active" height="185" width="537" layout="absolute" >
<mx:Button label="BUTTON1" />
<mx:Button label="BUTTON2" x="33" y="41"/>
<mx:Button label="BUTTON3" x="118" y="81"/>
<mx:Button label="BUTTON4" x="234" y="41"/>
<mx:Button label="BUTTON5" x="394" y="81"/>
</mx:Panel>
</mx:Application>
运行结果:
3.2 利用控件展示页面内容
FLEX标准组件库中有几十种丰富的控件库,有了这些搭配容器组件我们可以很方便的建立起十分绚丽的表现层界面,使我们的RIA应用真正地富起来。所有的控件类以及容器类都有共同的祖先UIComponent,我们将在后续自定义组件章节里详细分析这个祖先的行为和属性。读者大可以参考ADOBE官方示例浏览这些丰富的控件分别可以做些什么。
3.3 FLEX名字空间
在本书第一篇中我们已经简要地介绍了FLEX名字空间的概念。那么本节将详细地罗列出在FLEX编程中所涉及的关于名字空间的点点滴滴。
从字面上来讲,采用名字空间的最大的一个好处是避免类名字冲突,第二个好处是实现逻辑有效隔离。FLEX名字空间的展现方式主要分为两种,一种是在MXML根标签内;另一种是在ACTIONSCRIPT的包名字定义中。我们在每一个MXML文件的根标签内都要声明至少一个FLEX标准类库的名字空间,如
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
Xmlns就是 XML NAME SPACE ,是XML名字空间的意思。冒号后面的mx是一个名字空间的索引,后面双引号内的内容就是FLEX标准类库的SCHEMA。在MXML自定义组件中我们也必须指定名字空间。如名字为CustomizedCombox.mxml的自定义组件,定义在{$CLASSPATH}/com/broadview/flexpractice/chapter3下面。
<mx:ComboBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:dataProvider>
<mx:String>1</mx:String>
<mx:String>2</mx:String>
<mx:String>3</mx:String>
</mx:dataProvider>
</mx:ComboBox>
那么定义好自定义组件以后,该怎么用呢?第一步应该在引用文件中加入自定义组件的名字空间。如
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:customized="com.broadview.flexpractice.chapter3.*">
<mx:Panel title="LAYOUT Example" status="Active" height="185" width="537" layout="absolute" >
<mx:Button label="BUTTON1" />
<mx:Button label="BUTTON2" x="33" y="41"/>
<mx:Button label="BUTTON3" x="118" y="81"/>
<mx:Button label="BUTTON4" />
<mx:Button label="BUTTON5" />
<customized:CustomizedCombox id="customizedCom"/>
</mx:Panel>
</mx:Application>
这里我们随意指定了这个自定义组件的名字空间的索引为customized,它所指向的为FLEX应用的CLASSPATH下该组件所在的包全名。那么在用这个组件是,是这样写的。
<customized:CustomizedCombox id="customizedCom"/>
所以通用的格式为
<namespace:ClassTag id = “id name”></namespace:ClassTag>
如果没有定义这个namespace,就是匿名名字空间 如
xmlns="com.broadview.flexpractice.chapter3.*"
那么在引用时就变成了
<ClassTag id = “id name”></ClassTag>
以上我们具体地讲解了在mxml中定义和利用名字空间的方法。相对地,在ACTIONSCRIPT类中也会用到名字空间。首先类定义的时候需要指定当前类所在的类包,也就是它的名字空间。再者,如果在类的定义中要关联、组合、聚合、合成到别的类包中的别的类,我们必须要用import关键字显式地导入这个类的名字空间。如果没有导入,在用到这个类时就必须在这个类前加上这个类的名字空间作为前缀。示例代码。比如我们有一个类MenuModuleViewHelper,
3.4 数据绑定概念
数据绑定是FLEX中一个全新而强大的概念,数据绑定机制允许在整个FLEX客户端应用的范围内,对于任何一个对象或其属性绑定了任何别的的对象或属性,当源对象(被绑定对象)的值改变时,目标对象(绑定对象)的值也相应的改变。如对象A绑定了对象B,当对象B的值发生了变化,那么同步地,对象A的值也会改变。绑定是这样一种拷贝触发器的概念,源对象的值变化时,触发器被触发,进而紧接着一个从变化后的源对象值到目标对象值的拷贝动作。另外绑定并不是任意的,标准只有一条就是绑定的源对象和目标对象数据类型应该相同。
FLEX中提供了三种数据绑定方式供开发者选择,它们分别是
一. 通过大括号{}实现绑定。
二.通过标签<mx:Binding>实现绑定。
三.通过mx.binding.utils.BindingUtils类实现绑定。
下面我们将就每一种绑定方式深入讨论:
3.4.1通过大括号{}实现绑定
先看一个例子,
<?xml version="1.0" encoding="utf-8"?>
<Application xmlns="http://www.adobe.com/2006/mxml" initialize="doInit()">
<Script>
<![CDATA[
[Bindable] public var dataSet:Array;
public function doInit():void{
dataSet = [
{Expenses:200,Amount:450},
{Expenses:300,Amount:600},
{Expenses:100,Amount:300},
{Expenses:400,Amount:500},
{Expenses:500,Amount:250},
{Expenses:200,Amount:700}
];
}
]]
</Script>
<CheckBox id="zero" selected="true" />
<CheckBox id="auto" selected="true" />
<HBox>
<Label text="min" />
<VSlider value="0" liveDragging="true" minimum="-5" maximum="5" />
<Label text="max" />
<VSlider value="1" liveDragging="true" minimum="-5" maximum="5" />
</HBox>
<ColumnChart id="chart" width="100%" height="100%" dataProvider="{dataSet}" >
<series>
<ColumnSeries yField="Amount" />
</series>
<verticalAxis>
<LinearAxis baseAtZero="{zero.selected}" autoAdjust="{auto.selected}" />
</verticalAxis>
<horizontalAxis>
<CategoryAxis categoryField="Expenses" />
</horizontalAxis>
</ColumnChart>
</Application>
分析一下,ColumnChart的dataProvider属性绑定一个可绑定变量
[Bindable] public var dataSet:Array;
<ColumnChart id="chart" width="100%" height="100%" dataProvider="{dataSet}" >
在应用初始化的时候事件initialize被触发,doInit()方法被调用,在这个方法里可绑定变量dataSet的值从null重新赋值为一个Array数组,像我们以上所说,ColumnChart的dataProvider是绑定对象,dataSet是源对象或者被绑定对象,当dataSet的值被改变时,ColumnChart的dataProvider的值也会响应地改变。
3.4.2通过标签<mx:Binding>实现绑定
对上面的例子做些修改,代码如下
<?xml version="1.0" encoding="utf-8"?>
<Application xmlns="http://www.adobe.com/2006/mxml" initialize="doInit()">
<Script>
<![CDATA[
[Bindable] public var dataSet:Array;
public function doInit():void{
dataSet = [
{Expenses:200,Amount:450},
{Expenses:300,Amount:600},
{Expenses:100,Amount:300},
{Expenses:400,Amount:500},
{Expenses:500,Amount:250},
{Expenses:200,Amount:700}
];
}
]]>
</Script>
<Binding source="dataSet" destination="chart.dataProvider"/>
<CheckBox id="zero" selected="true" />
<CheckBox id="auto" selected="true" />
<HBox>
<Label text="min" />
<VSlider value="0" liveDragging="true" minimum="-5" maximum="5" />
<Label text="max" />
<VSlider value="1" liveDragging="true" minimum="-5" maximum="5" />
</HBox>
<ColumnChart id="chart" width="100%" height="100%" >
<series>
<ColumnSeries yField="Amount" />
</series>
<verticalAxis>
<LinearAxis baseAtZero="{zero.selected}" autoAdjust="{auto.selected}" />
</verticalAxis>
<horizontalAxis>
<CategoryAxis categoryField="Expenses" />
</horizontalAxis>
</ColumnChart>
</Application>
改动两个地方,一是去掉ColumnChart的dataProvider=”{dataSet}”, 然后增加了一行
<Binding source="dataSet" destination="chart.dataProvider"/>
Binding标签作用是建立起源对象和目标对象的绑定关系,目标对象随元目标值的改变而改变。
3.4.3通过mx.binding.utils.BindingUtils类实现绑定
BindingUtils共有两个公共方法bindProperty()和bindSetter(),分别用来绑定对象属性和绑定函数输入参数两个例子为
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.binding.utils.*;
public function init():void {
BindingUtils.bindProperty(q1, "text", q2, "text");
}
]]>
</mx:Script>
<mx:TextInput id="q1"/>
<mx:TextInput id="q2" />
</mx:Application>
ID为q2的text属性被绑定到ID为q1的text属性上,也就是说当往q2输入任何东西,q1都同步地跟随变化。这就是所谓BindingUtils的属性绑定。
第二个例子,
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.binding.utils.*;
public function init():void {
//BindingUtils.bindProperty(q1, "text", q2, "text");
BindingUtils.bindSetter(doSomething,q1,"text");
}
public function doSomething(text:String):void{
q2.text = text.length+'';
}
]]>
</mx:Script>
<mx:TextInput id="q1"/>
<mx:TextInput id="q2" />
</mx:Application>
应用初始化时init方法被调用里面调用BindingUtils的bindSetter方法,将q1的text属性绑定到函数doSomething的输入形参text上,并调用doSomething方法在q2中显示q1输入的字符长度。
3.5 RPC远程业务
FLEX提供RPC远程过程调用以便远程存取服务端数据集或者更新数据到远程服务端。本身FLEX有几种类型的RPC远程业务,如可以通过简单对象存储协议SOAP访问WEB SERVICE;通过AMF(Action Message Format)访问驻留在服务端的JAVA对象;或者通过HTTP URL获取服务端XML格式化数据。FLEX封装了所有的RPC远程业务到MXML标签中,也就是说我们可以在MXML标签或者ACTIONSCRIPT中定义我们的远程业务访问逻辑。它们分别为,
一 WEB SERVICE组件,通过SOAP协议远程访问服务端。
二 HTTPService组件,通过HTTP URL远程访问。
三 Remote Object 组件,这是Flex各RPC业务中最常用到的方式,因为它可以通过AMF协议,远程访问服务端的各种类型对象,如JAVA,PHP,.NET等。
下面我们将通过简单的实例展示上面的各种方式。
3.5.1 WEB SERVICE组件,通过SOAP协议远程访问服务端
毫无疑问,FLEX 的WEB SERVICE组件,总是扮演请求WEB服务的客户端角色。它是不能发布任何WEB服务的。假设BROADVIEW公司有一个WEB服务,输入身份证号码就可以拿到这个人的详细信息,那么现在来演示我们客户端的代码怎么写。
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:WebService id="IDService"
wsdl="http://broadview.com/ws/IDService?wsdl" useProxy="false">
<mx:operation name="GetMyID">
<mx:request>
<IDNumber>{id1.text}</IDNumber>
</mx:request>
</mx:operation>
</mx:WebService>
<mx:Panel paddingTop="10" paddingBottom="10" paddingLeft="10" paddingRight="10" >
<mx:TextInput id="id1" width="200" text="你的身份证号码?"/>
<mx:Button width="60" label="SEND" click="IDService.GetMyID.send();"/>
<mx:Label text="DETAIL INFO:"/>
<mx:TextArea text="{IDService.GetMyID.lastResult.detailInfo}"/>
</mx:Panel>
</mx:Application>
首先注意到代码中利用WEB服务组件绑定了一个名字叫做IDService的WEB服务,它在UDDI服务器注册的WSDL为http://broadview.com/ws/IDService?wsdl, 并且把客户端用户输入的身份证号码绑定到WEB服务的GetMyID操作上。
<mx:Button width="60" label="SEND" click="IDService.GetMyID.send();"/>
从这行代码可以看出,点击SEND按钮,IDService的GetMyID服务请求将被发出,通过UDDI路由到具体发布这个WEB服务的业务服务器,并异步地响应返回数据对象detailInfo.
<mx:TextArea text="{IDService.GetMyID.lastResult.detailInfo}"/>
3.5.2 HTTPService组件,通过HTTP URL远程访问
假使BROADVIEW公司CRM上面有一个这样的HTTP服务,通过提交输入的用户名和旧密码就可以取回服务器生成的一个新密码。这个客户端请求代码大体上应该这么写,
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
xmlns="*" creationComplete="submit()">
<mx:Script>
<![CDATA[
private function submit():void {
request.send();
}
]]>
</mx:Script>
<mx:Form x="22" y="10" width="500">
<mx:HBox>
<mx:Label text="Username"/>
<mx:TextInput id="username"/>
</mx:HBox>
<mx:HBox>
<mx:Label text="Old Password"/>
<mx:TextInput id="password"/>
</mx:HBox>
<mx:Button label="Submit" click="submit()"/>
</mx:Form>
<mx:DataGrid id="d" x="22" y="128" dataProvider="{request.lastResult.users.user}">
<mx:columns>
<mx:DataGridColumn headerText="New Password" dataField="password"/>
<mx:DataGridColumn headerText="User Name" dataField="username"/>
</mx:columns>
</mx:DataGrid>
<mx:HTTPService id="request" url="http://broadview.com/flexpractice/httpservice.jsp" useProxy="false" method="POST">
<mx:request xmlns="">
<username>{username.text}</username>
<password>{password.text}</password>
</mx:request>
</mx:HTTPService>
</mx:Application>
首先定义一个ID为request的HTTP服务请求,初步设定利用POST方法请求http://broadview.com/flexpractice/httpservice.jsp 并绑定用户输入的用户名和旧密码作为POST请求参数。点击Submit按钮时,submit方法被调用,发送请求。DataGrid组件用于绑定服务端返回的新密码。
<mx:DataGrid id="d" x="22" y="128" dataProvider="{request.lastResult.users.user}">
3.5.3 Remote Object 组件
假设我们在服务端有一个JAVA的systemService,这个服务中有个getList()方法,输入用户名和密码就可以拿到这个人当年的奖惩列表。 首先应该在remoting-config.xml注册这个远程服务。
<?xml version="1.0" encoding="UTF-8"?>
<service id="remoting-service"
class="flex.messaging.services.RemotingService"
messageTypes="flex.messaging.messages.RemotingMessage">
<adapters>
<adapter-definition id="java-object" class="flex.messaging.services.remoting.adapters.JavaAdapter" default="true"/>
</adapters>
<default-channels>
<channel ref="my-amf"/>
</default-channels>
<destination id="systemService">
<properties>
<factory>spring</factory>
<source>systemMgmtService</source>
</properties>
</destination>
</service>
客户端代码为,
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" verticalGap="10">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
[Bindable]
public var list:Object;
]]>
</mx:Script>
<mx:RemoteObject
id="performance"
destination="systemService"
result="list=event.result"
fault="Alert.show(event.fault.faultString, 'Error');"/>
<mx:HBox>
<mx:Label text="Username"/>
<mx:TextInput id="username"/>
</mx:HBox>
<mx:HBox>
<mx:Label text="Password"/>
<mx:TextInput id="password"/>
</mx:HBox>
<mx:Label text="{list.toString()}"/>
<mx:Button click="performance.getList(username.text,password.text);"/>
</mx:Application>
关注RomoteObject标签,destination属性是远程服务对象的名字,也就是配在remote-config.xml中的SERVICE名字,result和fault两个属性分别定义了两个回调句柄,数据成功返回时result句柄指定的回调函数或操作被执行,数据返回失败时,fault句柄指定的回调函数或操作被执行。
3.6 数据格式化和数据验证
原则上我们经常会对表单做数据验证,尤其对数据的格式有要求的时候,这个过程应该在客户端完成,没有必要提交不合格的数据到服务器端验证。FLEX提供多种数据验证组件,如字符串验证,日期验证,电话号码验证等等。FLEX的标准验证组件中有三个经常用到的属性和一个函数,它们分别是,required属性绑定一个布尔值,用于说明当前绑定验证对象是必须不为空的还是可以为空;source属性用于指定被绑定的验证对象ID如TextInput组件的ID等;property属性用于指定对被绑定对象的那个属性进行验证如TextInput的text属性等。一个函数是验证组件的validate()方法,此方法执行后会触发一个验证结果事件ValidationResultEvent,通过检测这个事件的类型为有效ValidationResultEvent.VALID或者是无效ValidationResultEvent.INVALID来最终判断是否提交数据到后台服务端。示例代码为,
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.events.ValidationResultEvent;
private var vResult:ValidationResultEvent;
private function validateAndSubmit():void {
vResult = fNameV.validate();
if (vResult.type==ValidationResultEvent.INVALID)
return;
vResult = lNameV.validate();
if (vResult.type==ValidationResultEvent.INVALID)
return;
vResult = dayV.validate();
if (vResult.type==ValidationResultEvent.INVALID)
return;
}
]]>
</mx:Script>
<mx:Model id="formInfo">
<formData>
<date>
<month>{monthInput.text}</month>
<day>{dayInput.text}</day>
<year>{yearInput.text}</year>
</date>
<name>
<firstName>{fNameInput.text}</firstName>
<lastName>{lNameInput.text}</lastName>
</name>
<phoneNum>{phoneInput.text}</phoneNum>
</formData>
</mx:Model>
<mx:StringValidator id="fNameV"
required="true"
source="{fNameInput}"
property="text"/>
<mx:StringValidator id="lNameV"
required="true"
source="{lNameInput}"
property="text"/>
<mx:PhoneNumberValidator id="pnV"
source="{phoneInput}"
property="text"/>
<mx:DateValidator id="dayV"
triggerEvent=""
daySource="{dayInput}" dayProperty="text"
monthSource="{monthInput}" monthProperty="text"
yearSource="{yearInput}" yearProperty="text"/>
<mx:Form>
<mx:FormItem label="Month">
<mx:TextInput id="monthInput"/>
</mx:FormItem>
<mx:FormItem label="Day">
<mx:TextInput id="dayInput"/>
</mx:FormItem>
<mx:FormItem label="Year">
<mx:TextInput id="yearInput"/>
</mx:FormItem>
<mx:FormItem label="First name">
<mx:TextInput id="fNameInput"/>
</mx:FormItem>
<mx:FormItem label="Last name">
<mx:TextInput id="lNameInput"/>
</mx:FormItem>
<mx:FormItem label="Phone">
<mx:TextInput id="phoneInput"/>
</mx:FormItem>
</mx:Form>
<mx:Button label="Submit" click="validateAndSubmit();"/>
</mx:Application>
运行结果:
Flex的标准类库还包含了一些数据格式化组件,它们的行为总是这样的。就是,预先指定这个格式化组件的合适规范,一般都通过stringFormat属性来指定。格式化动作通过调用其公共方法format()来实现,输入参数为一个粗粒度的对象,格式化后的结果遵循stringFormat定义的schema.示例代码,
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
private function format():void
{
formatted.text=dateFor.format(textinput.text);
}
]]>
</mx:Script>
<mx:DateFormatter id="dateFor" formatString="month: MM, day: DD, year: YYYY"/>
<mx:Panel width="549" height="212"
paddingTop="10" paddingLeft="5" paddingRight="5" paddingBottom="10">
<mx:Form width="495" height="141">
<mx:FormItem label="Enter date (mm/dd/yyyy):" width="432">
<mx:TextInput id="textinput" text=""/>
</mx:FormItem>
<mx:FormItem label="Formatted: " width="433">
<mx:TextInput id="formatted" text="" editable="false"/>
</mx:FormItem>
<mx:FormItem>
<mx:Button label="Format" click="format();"/>
</mx:FormItem>
</mx:Form>
</mx:Panel>
</mx:Application>
点击Format按钮输入日期从一种格式转换成另一种格式。
运行结果:
3.7 样式表CSS
在HTML中我们经常会用CSS样式表统一设定页面元素的风格。同样地,FLEX也用CSS样式表来风格化页面元素。在FLEX的应用中可以用<mx:Style>标签内嵌地设定页面风格,同时也可以引用外部CSS样式表。另外<mx:Style>标签只能定义在MXML文件根节点的第一层孩子节点当中。总体上说CSS风格定义可分为类选择器和类型选择器,类选择器方式只对一个组件元素有效,类型选择器则对当前这一种类型的页面元素都有效。举例说明,
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Style>
.fontStyle {
fontSize: 15;
color: #9933FF;
}
Label {
fontStyle: italic;
}
</mx:Style>
<mx:Button id="myButton" styleName="fontStyle" label="Click"/>
<mx:Label text="test">
</mx:Label>
</mx:Application>
在<mx:Style>标签内,.fontStyle定义方式属于样式类在FLEX组件中通过styleName属性引用,而Label则属于样式类型,对当前MXML文件所有的Label页面元素都有效。
运行结果:
同样地,在ACTIONSCRIPT中修改更新或重新定义样式表,FLEX标准可视化组件都包含两个公共方法setStyle()/getStyle()用于设置或者得到当前页面元素的风格样式。参考下例,
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Style>
Button {
fontSize: 10pt;
color: Blue;
}
.textInputClass {
fontFamily: Arial, Helvetica, "_sans";
color: Red;
fontSize: 22;
fontWeight: bold;
}
</mx:Style>
<mx:Script><![CDATA[
public function showStyles():void {
label1.text = String(textInput1.getStyle("fontSize"));
}
public function setNewStyles(newSize:Number):void {
label1.setStyle("fontSize",newSize);
}
]]></mx:Script>
<mx:VBox id="vbox">
<mx:TextInput styleName="textInputClass" text="HELLO CSS!" id="textInput1"
width="400"/>
<mx:Label id="label1" text="" width="400"/>
<mx:Button label="Get Style" click="showStyles();"/>
<mx:Button label="Set Style" click="setNewStyles(Number(textInput2.text));"/>
<mx:TextInput text="" id="textInput2" width="50"/>
</mx:VBox>
</mx:Application>
点击Set Style按钮setNewStyles()方法被调用,重新设置字体大小。
<mx:Button label="Set Style" click="setNewStyles(Number(textInput2.text));"/>
运行结果为:
对于样式表的操作还有一种情形就是一次性改变<mx:Style>标签内的风格属性,这个时候要用到风格管理器StyleManager,通过getStyleDeclaration()方法拿到内嵌样式对象实例,然后再设置样式对象的实例。参考示例,
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Style>
.style {
fontSize: 15;
color: #9933FF;
}
</mx:Style>
<mx:Script><![CDATA[
public function changeStyles(e:Event):void {
StyleManager.getStyleDeclaration('.style').setStyle('color',0x3399CC);
StyleManager.getStyleDeclaration('.style').setStyle('fontSize',25);
}
]]></mx:Script>
<mx:Button id="button1" label="Change1" styleName="style" click="changeStyles(event)"/>
<mx:Button id="button2" label="Change2" styleName="style" />
</mx:Application>
运行结果为
3.8 EFFECTS行为
在FLEX开发过程中我们为了优化应用的响应方式,通常要对EFFECTS行为类进行操作。FLEX本身提供了若干标准的EFFECTS类,如,ZOOM,FADE等。EFFECTS类通常被一些用户动作触发,如鼠标移动,键按下,得到焦点等等。关于EFFECTS的话题大体可分为三个方面。
一 事件触发EFFECTS
二 以声明的方式自定义EFFECTS
三 以编程的方式自定义EFFECTS
3.8.1事件触发EFFECTS
如前所述,EFFECTS类通常被用户事件触发,比如所有FLEX可视化组件都包含三个属性showEffect,hideEffect和moveEffect。它们分别表示当前组件显示、隐藏、移动时所要触发的EFFECT句柄。参考示例,
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" backgroundAlpha="0">
<mx:HBox horizontalGap="0">
<mx:VBox horizontalAlign="center">
<mx:Image source="@Embed('N1.png')"
showEffect="Fade"
hideEffect="Fade"
visible="{c1.selected}"/>
<mx:CheckBox id="c1" label="visible" selected="true"/>
</mx:VBox>
<mx:VBox horizontalAlign="center">
<mx:Image source="@Embed('N2.png')"
showEffect="WipeRight"
hideEffect="WipeLeft"
visible="{c2.selected}"/>
<mx:CheckBox id="c2" label="visible" selected="true"/>
</mx:VBox>
</mx:HBox>
</mx:Application>
Image的visible绑定到CheckBox是否被选中属性。当组件隐藏时,hideEffect被触发;当组件显示时,showEffect被触发。运行结果:
3.8.2以声明的方式自定义EFFECTS
在一般的应用中,我们都是利用EFFECTS组件标签显式地声明将被绑定和触发的效果EFFECTS类。在这类标签中有一个target属性用来绑定将要触发此效果的页面对象元素。示例代码,
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" backgroundAlpha="0">
<mx:Script>
[Bindable]
public var angle:int=0;
private function doRotate() : void
{
angle += 140;
rotate.play();
}
</mx:Script>
<mx:Rotate id="rotate" angleFrom="{angle-145}" angleTo="{angle}" target="{img}"/>
<mx:Panel width="229" height="231" horizontalAlign="center">
<mx:Canvas id="canvas" width="169" height="138" verticalScrollPolicy="off">
<mx:Image id="img" x="75" y="60" source="@Embed('N1.png')"/>
</mx:Canvas>
<mx:ControlBar width="219">
<mx:Button label="Rotate" click="doRotate()"/>
</mx:ControlBar>
</mx:Panel>
</mx:Application>
点击旋转按钮,doRotate()方法被调用,rotate效果类被触发。运行结果:
3.8.3以编程的方式自定义EFFECTS
另外也可以在ACTIONSCRIPT中,动态地定义EFFECTS类。示例代码,
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" backgroundAlpha="0">
<mx:Script>
import mx.effects.Move;
private function moveImage(xTo:int, yTo:int):void
{
var m:Move = new Move(img1);
m.xTo = xTo;
m.yTo = yTo;
m.play();
}
</mx:Script>
<mx:Panel width="467" height="302"
horizontalAlign="center">
<mx:Canvas id="canvas" width="423" height="248" mouseDown="moveImage(mouseX - 110, mouseY - 10)">
<mx:Image id="img1" source="@Embed('N1.png')" x="192.5" y="92"/>
</mx:Canvas>
</mx:Panel>
</mx:Application>
当鼠标左键在图片上按下时moveImage()方法被触发。先NEW 一个Move效果类实例然后初始化移动终点的坐标值,调用play()方法触发。
运行结果:
3.9 自定义组件
Flex组件开发可分为两种. 一是在mxml中创建自定义组件.另一种则在actionscript class中创建. 总体说来其实大同小异. 首先我们要转换一种观点. mxml组件文件和ActionScript class文件一样都是类.开发者都可以在语法和机制上new 这个类的. 比如两个一模一样的组件.MyButton.mxml 和 MyButton.as. 当我们想要在某一个as函数中动态的创建这个自定义按钮时,都可以.
public var tempButton:MyButton = new MyButton();
parentPanel.addChild(tempButton);
当我们自定义组件时,有若干问题要注意.
1. 如果我们想要给这个新组件添加一个属性,只要在组件类中声明这个变量为public就可以了.
2. 如果想要给这个组件添加一个自定义事件,只要在组件类定义这个Event然后addEventListener就可以了.
3. 如果此组件需要一些Util工具函数,只要在组件定义类内部把这个工具函数public static就可以了.
4. 如果你的组件比较复杂并且存在数据相互依存,那么建议最好将组件的createPolicy设置为"all", default为"auto",
在ACTIONSCRIPT中定义一个扩展的TextArea组件CustomTextArea,用来监控键盘按键操作。代码为
package com.broadview.flexdevpractice.chapter3
{
import mx.controls.TextArea;
import flash.events.KeyboardEvent;
public class CustomTextArea extends TextArea {
public function CustomTextArea() {
super();
addEventListener("keyDown", keyDown);
addEventListener("keyUp", keyUp);
}
private function keyDown(eventObj:KeyboardEvent):void {
text = eventObj.charCode+'';
}
private function keyUp(eventObj:KeyboardEvent):void {
text += eventObj.toString()+'/n';
}
}
}
主应用文件代码,注意名字空间的引用。
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:Comp="com.broadview.flexdevpractice.chapter3.*">
<Comp:CustomTestArea id="customArea" height="282" width="322"/>
</mx:Application>
运行结果为:
3.10 MXML组件对象树的遍历和引用
MXML文件内所包含的组件是一颗组件对象树,那么该怎么遍历和查询树中的任意一个节点呢?方式有若干,分别为。
一 如果知道对象组件id的名字,那么可以在ACTIONSCRIPT和绑定的{}内任意引用。
二 如果对象组件ID属性没有显式定义,那么可以通过其父容器的getChildAt()和getChildByName()方法来实现。参考示例,
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.controls.TextInput;
import mx.core.UIComponent;
public function doClick():void{
var objs:Array = box1.getChildren();
var str:String = '';
for(var i:int=0;i<objs.length;i++){
str+=UIComponent(objs[i]).name + '|'+UIComponent(objs[i]).className+'|';
}
TextInput(box1.getChildAt(1)).text = str;
TextInput(box1.getChildByName(box1.getChildAt(2).name)).text = str;
}
]]>
</mx:Script>
<mx:HBox width="499" height="198">
<mx:Panel title="Panel1" width="494" height="188">
<mx:VBox id="box1" paddingTop="8" paddingBottom="8" paddingLeft="8" paddingRight="8" horizontalGap="4" width="452" height="97">
<mx:Label text="LABEL"/>
<mx:TextInput width="382"/>
<mx:TextInput width="382"/>
</mx:VBox>
<mx:Button id="but" click="doClick()" label="IDList" />
</mx:Panel>
</mx:HBox>
</mx:Application>
运行结果:
三 利用this关键字引用
在MXML文件中,关键字this代表当前MXML文件的对象实例。我们可以用this[“ID名字”]的格式来引用MXML标签内的内嵌元素。示例代码,
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.controls.TextInput;
public function click():void{
TextInput(this['firstName']).text = TextInput(this['firstName']).className;
}
]]>
</mx:Script>
<mx:HBox width="365" height="165">
<mx:Panel title="Panel 1" width="357" height="152">
<mx:HBox paddingTop="8" paddingBottom="8" paddingLeft="8" paddingRight="8" horizontalGap="4" width="324" height="52">
<mx:Label text="First Name:"/>
<mx:TextInput id="firstName" width="219"/>
</mx:HBox>
<mx:Button label="Check" click="click()"/>
</mx:Panel>
</mx:HBox>
</mx:Application>
运行结果:
四 合理理解parentApplication,parentDocument和Application.application.
在实际的FLEX应用中,情况可能是非常复杂的。可能定义了许多各式各样的MODULE文件,它是以SWF字节码的方式加载到主应用中来的;还可能自定义了很多MXML格式或者ACTIONSCRIPT格式的组件,问题是我们经常会遇到该怎么在一个单独模块中访问另一个模块中组件的ID等等,这是FLEX开发者都会遇到的问题。
实际上,FLEX的标准实现提供已经提供了一个方案,就是利用parentApplication,parentDocument或者Application.application在整个应用的对象组件树中索引目标组件ID。我们都知道,父节点机制是树这种数据结构的一种通用遍历方式。在FLEX的根节点是Application,一般是个MXML的主文件,在它的下面会包含很多MODULES,自定义组件等,这总体上可分为两种,对象实例和对象文档。对象实例方式用当前组件内置parentApplication属性访问父节点对象ID;相应地对象文档用parentDocument方式访问父节点对象ID。对象文档被集成到主应用后又兼备了对象实例的角色,也就是说自定义组件不管是在MXML中定义的还是在ACTIONSCRIPT中定义的或者是MODULE组件都是对象实例也是对象文档,同时可以用parentApplication和parentDocument两种属性访问父节点组件ID。
FLEX为每一个MXML格式的和ACTIONSCRIPT格式的自定义组件都创建了一个DOCUMENT对象。相对于自定义组件,parentApplication和parentDocument都是可以递归引用的,如parentDocument.parentDocument.parentDocument如此等等。FLEX的每个可视化组件都有一个isDocument()方法,用来判断当前组件是否是一个对象文档。对于parentDocument和parentApplication是一种自底而上的访问方式,而Application.application则是一种自顶而下的方式Application.application属性实际上是整个主文件的实例对象ID索引。下面将分别就此三种方式展示一个实例。
实例1:MXML自定义组件中利用parentApplication访问父节点对象。
自定义组件代码CustomBox.mxml,
<?xml version="1.0" encoding="utf-8"?>
<mx:Box xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300" creationComplete="dataFromParent()">
<mx:Script><![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var e1:ArrayCollection;
private function dataFromParent():void {
e1 = parentApplication.e1;
}
]]></mx:Script>
<mx:ColumnChart id="chart" dataProvider="{e1}" height="302">
<mx:horizontalAxis>
<mx:CategoryAxis
dataProvider="{e1}"
categoryField="M"
/>
</mx:horizontalAxis>
<mx:series>
<mx:ColumnSeries
xField="M"
yField="P"
displayName="P"
/>
<mx:ColumnSeries
xField="M"
yField="E"
displayName="E"
/>
</mx:series>
</mx:ColumnChart>
<mx:Legend dataProvider="{chart}"/>
<mx:Button id="b1" click="e1 = parentApplication.getCollection();" label="Get Data"/>
</mx:Box>
主应用文件TestParentApplication.mxml,
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:custombox="*">
<mx:Script><![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
public var e1:ArrayCollection = new ArrayCollection([
{M:"z", P:200, E:150},
{M:"x", P:100, E:20},
{M:"c", P:150, E:50}
]);
public function getCollection():ArrayCollection {
return new ArrayCollection([
{M:"v", P:100, E:100},
{M:"b", P:100, E:50},
{M:"n", P:100, E:60}
]);
}
]]></mx:Script>
<custombox:CustomBox id="test" height="398"/>
</mx:Application>
运行结果,
实例1:MXML自定义组件中利用parentDocument访问父节点文档对象
和上例相同只是把上例的parentApplication改成parentDocument就可以了,看上去是,
<mx:Script><![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var e1:ArrayCollection;
private function dataFromParent():void {
e1 = this.parentDocument.e1;
}
]]></mx:Script>
<mx:Button id="b1" click="e1 = this.parentDocument.getCollection();" label="Get Data"/>
实例三:MXML 自定义MODULE中利用parentDocument访问父节点文档对象。
自定义MODULE CustomBox.mxml(注意MODULE加载方式,MODULE窗口最大化要用percentWidth="100" percentHeight="100"),这里换上parentApplication都是一样的。
<?xml version="1.0" encoding="utf-8"?>
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" percentWidth="100" percentHeight="100" creationComplete="dataFromParent()">
<mx:Script><![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var e1:ArrayCollection;
private function dataFromParent():void {
e1 = this.parentDocument.e1;
}
]]></mx:Script>
<mx:ColumnChart id="chart" dataProvider="{e1}" height="302">
<mx:horizontalAxis>
<mx:CategoryAxis
dataProvider="{e1}"
categoryField="M"
/>
</mx:horizontalAxis>
<mx:series>
<mx:ColumnSeries
xField="M"
yField="P"
displayName="P"
/>
<mx:ColumnSeries
xField="M"
yField="E"
displayName="E"
/>
</mx:series>
</mx:ColumnChart>
<mx:Legend dataProvider="{chart}"/>
<mx:Button id="b1" click="e1 = this.parentDocument.getCollection();" label="Get Data"/>
</mx:Module>
相应地,主文件TestModuleParentDocument.mxml
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:custombox="*">
<mx:Script><![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
public var e1:ArrayCollection = new ArrayCollection([
{M:"z", P:200, E:150},
{M:"x", P:100, E:20},
{M:"c", P:150, E:50}
]);
public function getCollection():ArrayCollection {
return new ArrayCollection([
{M:"v", P:100, E:100},
{M:"b", P:100, E:50},
{M:"n", P:100, E:60}
]);
}
]]></mx:Script>
<mx:ModuleLoader url="CustomBox.swf" height="392" width="442"/>
</mx:Application>
3.11 事件模型
3.11.1事件处理
FLEX事件分为用户事件和系统事件两种类型。从简单的按钮点击的单击用户交互事件到远程数据流加载完毕的系统事件,都属于FLEX事件体系的一部分。在FLEX中每个事件都由一个事件对象来封装。所有事件类都是Event基础类的子类。每个事件对象中都包含了一系列事件处理方法。用户或系统动作触发事件,事件对象被创建,FLASH PLAYER9随即将该对象派发到事件的目标对象。如果事件目标对象属于页面可视化对象,那么该事件会被逐次地从显示列表层次结构中向下路由到最终目标对象。事件获得响应后,该事件一般会以相同的路径返回。这就是所谓的事件流。事件的目标对象通常使用注册事件监听器的方式捕捉并响应该事件,响应的意思就是执行一次注册在事件监听器中的事件响应函数。参考示例,
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script><![CDATA[
private function clickHandler(event:Event):void {
mx.controls.Alert.show("Button is clicked");
}
]]></mx:Script>
<mx:Button id='b'
label="Click"
initialize='b.addEventListener(MouseEvent.CLICK, clickHandler, false, 1);'
/>
</mx:Application>
按钮在初始化时注册事件监听器,并设置clickHandler()方法作为事件响应函数。
ACTIONSCRIPT3的事件模型和早期历史版本的事件模型很不一样。它采用了单一事件处理模型,基于文档对象模型DOM的第3级事件模型。
3.11.2 事件流
我们都知道,FLEX应用的表现层布局基本上是一个基于可视化和非可视化组件的树形结构。应用中每一个组件对象都可能是事件的目标对象。在ACTIONSCRIPT3的事件模型中,采用事件流的方式遍历整个应用组件树直到找到注册过这个事件监听器的目标对象,这就是所谓的捕捉阶段。目标对象捕捉这个事件后,执行侦听函数中定义的逻辑,这就是目标阶段。事件响应后,该事件最后会沿着捕捉路线回溯到舞台,这就是所谓的冒泡阶段。舞台Stage定义在flash.display.Stage,它位于应用显示列表层次的顶部,是一种特殊的显示对象容器。参考图4.-2-1
3.11.3事件对象
在编程中我们会经常碰到关于事件的处理,在这之前很有必要清楚事件对象的内部结构和常用的处理方式。FLEX所有类型的事件都继承自flash.events.Event类。它包含了一系列有用的属性和方法。
首先介绍事件对象的几个常用的属性。
1 type 指事件对象的类型,有时我们会根据事件的类型来做一些动作
2 target 指事件对象所绑定的目标对象ID, 这是EVENT类中最有用的属性。
3 bubbles 此属性包含事件流中事件对象参与的部分的信息。
4 eventPhase 此属性指示事件流中当前所处的阶段。
5 currentTarget 此属性存储对当前正在处理事件对象的显示列表对象的引用。
下面通过几个示例来具体展示这些属性的用法。
示例1: type属性
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" initialize="addListeners()">
<mx:Script><![CDATA[
import mx.controls.Alert;
private function handler(e:Event):void {
if(e.type == "click"){
mx.controls.Alert.show('BUTTON WAS CLICKED');
}else{
mx.controls.Alert.show('BUTTON WAS MOUSED OVER');
}
}
private function addListeners():void {
b1.addEventListener(MouseEvent.CLICK, handler);
b1.addEventListener(MouseEvent.MOUSE_OVER, handler);
}
]]></mx:Script>
<mx:VBox id="vb1">
<mx:Button id="b1" label="Click Me"/>
</mx:VBox>
</mx:Application>
通过判断事件的类型可以控制分别执行不同的操作。
示例2 target 和 currentTarget属性
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()">
<mx:Script><![CDATA[
import mx.controls.Button;
[Bindable]private var times:Number=0;
private function init():void {
l1.addEventListener(MouseEvent.CLICK, dosomething);
l2.addEventListener(MouseEvent.CLICK, dosomething);
}
private function dosomething(event:MouseEvent):void {
if (event.shiftKey ) {
times++;
} else {
event.currentTarget.toolTip = "HOLD THE SHIFT KEY";
}
}
]]></mx:Script>
<mx:Label id="l1" text="{'You have clicked '+ times+' times'}" />
<mx:Label id="l2" text="{'You have clicked '+ times+' times'}" />
</mx:Application>
按住SHIFT键点击LABEL统计点击的次数,通过target和currentTarget属性可以分别拿到当前事件目标对象的ID索引。
3.12 总结
本章就FLEX编程结合示例展示了一系列基础的核心主题。下一章我们将要介绍关于构建高效率用户界面的专题。