Flex MXML初探
MXML
MXML是Adobe为Flex推出的一套基于XML的标记语言,目的在于简化Flex的UI开发。实际功能则不仅仅局限于此,除了可以实现布局、控件等UI之外,还可以实现数据定义、与服务器交互等功能。MXML中的标签基本上都与Flex类库中的某个类相关联的(编译器标签除外),这一点从framework中的mxml-manifest.xml文件中可以看到。
<component id="Array" class="Array" lookupOnly="true"/> <component id="ArrayCollection" class="mx.collections.ArrayCollection"/> <component id="BevelFilter" class="flash.filters.BevelFilter" lookupOnly="true"/> <component id="BitmapFill" class="mx.graphics.BitmapFill"/> <component id="Blur" class="mx.effects.Blur"/> <component id="BlurFilter" class="flash.filters.BlurFilter" lookupOnly="true"/> <component id="Boolean" class="Boolean" lookupOnly="true"/> <component id="Box" class="mx.containers.Box"/> <component id="Button" class="mx.controls.Button"/> <component id="ButtonBar" class="mx.controls.ButtonBar"/> <component id="Canvas" class="mx.containers.Canvas"/> <component id="CheckBox" class="mx.controls.CheckBox"/>
以上截取了mxml-manifest.xml中的部分内容,MXML中的每一个标签都被申明成一个component,而ID则是MXML标签的标签名,class则是与之对应的类库中的类。也就是说,MXML实际上只是将类库XML化了而已,这样更便于实现可视化的UI设计。但最终MXML都是会被编译成ActionScript的。下面来看一个完整的MXML例子。
<?xml version="1.0"?> <mx:Application xmlns:mx="https://www.adobe.com/2006/mxml" layout="absolute"> <mx:Panel title="My Application" height="128" x="226" y="24" layout="absolute"> <mx:TextArea id="textarea1" width="155" x="0" y="0"/> <mx:Button label="Click Me" click="textarea1.text='Hello World';" width="92" x="31.5" y="56" /> </mx:Panel> </mx:Application>
需要说明的是在Application标签中申明了一个XML命名空间,即xmlns:mx=”https://www.adobe.com/2006/mxml”,这个命名空间包含所有官方MXML的标签。在这个文件中所有的标签都是以”mx:”开头,表示引用的就是mx命名空间下的相应类。
属性设置方式
MXML中设置一个标签的属性有两种等价的方式,一种是直接作为标签的属性来设置,另一种是作为标签的子标签(结点)来设置。相比之下,在不引入外部文件的情况下,第二种方式扩展性更好,因为标签可以一级一级的嵌套。当然,单纯作为属性来设置也是可以实现的,只不过需要把相应的值放到外部文件中作为component来引入。以下是一个实例。
<mx:Label text="这里直接在text属性里设置"/> <mx:Label> <mx:text> 这里是作为子标签来设置的,如果是标签的话可以继续扩展。 </mx:text> </mx:Label>
编译器标签(Compiler Tags)
所谓的编译器标签是指那些MXML中并没有实际的类库类与之对应的标签,这些标签的存在是为了向编译器指明编译的方式。在MXML中主要包括以下的编译器标签。
<mx:Binding>
-用于绑定两个对象的数据,这个标签只是定义了两个对象的数据间的关系,编译后是不存在这个标签对应的类的。
<mx:Component>
-可以用来定义一个组件作为控件的渲染器或者编辑器,包括两种方式:内联和外链文件,前者创建的组件是在当前作用域,而通过外链文件创建的组件则是全新的作用域。
<mx:Metadata>
-用于添加元素据。
<mx:Model>
-用于在MXML中申明数据模型,编译后的形式是一棵ActionScript的对象树,树的叶子节点都是数值型的数据。
<mx:Script>
-定义或引入ActionScript。
<mx:Style>
-定义或引入样式。
<mx:XML>
-定义XML结构的数据对象。
<mx:XMLList>
-定义E4X XMLList的对象。
此外还有以下几个与Server通信相关的几个标签。
<mx:operation>
<mx:request>
<mx:method>
<mx:arguments>
一些MXML标签规则
- 对于所有的标签,id属性都不是必须的。
- 对于根标签,是不能设置id的。
- Boolean属性只包括true或flase两种值。
- <mx:Binding>标签必须包含source和destination属性。
- <mx:Binding>不能设置id属性。
- <mx:WebService>必须设置一个wsdl或一个destination属性,但不能二者都设置。
- <mx:RemoteObject>必须设置一个source或一个name属性,但不能二者都设置。
- <mx:HTTPService>必须设置一个url或一个destination属性,但不能二者都设置。
- <mx:operation>必须设置一个name属性,但是name不能重复,此外该标签不能设置id属性。
- <mx:method>同<mx:operation>
ActionScript
在Flex开发中,MXML可以胜任一些UI上的任务,但是对于业务逻辑方面就需要ActionScript了。ActionScript是一种面向对象的过程式语言,它基于ECMAScript(ECMA-262)4的语言规范。在实际的开发中,ActionScript与MXML可以混合使用:
- 可以在MXML标签的的事件属性中使用ActionScript来定义事件处理器。
- 可以在MXML文件中使用<mx:Script>标签添加内联的ActionScript。
- 可以在MXML文件中引用外部ActionScript文件。
- 可以import外部的ActionScript类。
- 可以创建ActionScript组件。
ActionScript的编译
对于一个简单的Flex应用,可能所有的代码都包含在一个MXML文件或ActionScript文件中,但对于大多数应用而言,往往都是由多个文件组成的。例如通常都会将<mx:Applciation>中的<mx:Script>和<mx:Style>部分分离出来作为独立的AS文件和CSS文件,然后在<mx:Application>中再包含这些文件。此外,程序中也经常会引入一些自定义的MXML或ActionScript组件,这些组件必须定义在其他独立的文件中,MXML组件还有可能包含其他的AS或CSS文件。组件除了可以是源码外,还可以是已经预编译的SWC文件。另外,包含可执行代码的SWF文件也可以被嵌入到程序中。一个应用程序中包含的所有以上提到的文件都将被编译到一个单独的SWF文件中。
Flex编译器在编译的时候,会将主MXML文件以及它所包含(指定Script标签的source方式)的其他文件(AS、CSS)都编译成一个单独的AS类。其中,AS文件中的import部分会被合并到编译结果类的import中,函数外的变量申明则会合并到编译结果类的成员申明中,而其他的函数则会直接拼接到类的实现中。而CSS文件中的定义也被编译成相应的AS代码。具体的可以看以下的例子:
主MXML文件(清单1)
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="https://www.adobe.com/2006/mxml" layout="absolute" xmlns:local="*"> <mx:Panel title="My Application" height="128" x="226" y="24" layout="absolute"> <mx:TextArea id="textarea1" width="155" x="0" y="0"/> <mx:Button label="Click Me" click="textarea1.text='Hello World';" width="92" x="31.5" y="56" /> <local:MyButton label="MyButton"/> </mx:Panel> <mx:Script source="MyAppAS.as"/> <mx:Style source="MyAppCSS.css"/> </mx:Application>
MyButton组件(清单2)
<?xml version="1.0" encoding="utf-8"?> <mx:Button xmlns:mx="https://www.adobe.com/2006/mxml"> </mx:Button>
MyAppAS.as(清单3)
// ActionScript file import https://varnow.org/files/mx.rpc.http.httpservice; public var t:int = 1; public function justATest():void{ var http:HTTPService = new HTTPService(); t = 2; }
MyAppCSS.css(清单4)
/* CSS file */ Button{ color: blue; font-size: 14px; }
在主MXML文件中,包含了一个Panel、一个Textarea、一个Button,以及一个组件MyButton,此外还引入的两个外部文件:MyAppAS.as和MyAppCSS.css。编译后,主MXML文件以及两个外部文件都被编译成一个单独的类,而MyButton组件被编译成另一个单独的类:
编译结果:MyApp类(清单5)
package { import https://varnow.org/files/myapp.*; import https://varnow.org/files/flash.events.*; import https://varnow.org/files/mx.containers.*; import https://varnow.org/files/mx.controls.*; import https://varnow.org/files/mx.core.*; import https://varnow.org/files/mx.events.*; import https://varnow.org/files/mx.rpc.http.*; import https://varnow.org/files/mx.styles.*; public class MyApp extends Application { public var t:int = 1; private var _1035784137textarea1:TextArea; private var _documentDescriptor_:UIComponentDescriptor; static var _MyApp_StylesInit_done:Boolean = false; public function MyApp() { _documentDescriptor_ = new UIComponentDescriptor({type:Application, propertiesFactory:function () : Object { return {childDescriptors:[new UIComponentDescriptor({type:Panel, propertiesFactory:function () : Object { return {title:"My Application", height:128, x:226, y:24, layout:"absolute", childDescriptors:[new UIComponentDescriptor({type:TextArea, id:"textarea1", propertiesFactory:function () : Object { return {width:155, x:0, y:0}; }// end function }), new UIComponentDescriptor({type:Button, events:{click:"___MyApp_Button1_click"}, propertiesFactory:function () : Object { return {label:"Click Me", width:92, x:31.5, y:56}; }// end function }), new UIComponentDescriptor({type:MyButton, propertiesFactory:function () : Object { return {label:"MyButton"}; }// end function })]}; }// end function })]}; }// end function }); mx_internal::_document = this; .mx_internal::_MyApp_StylesInit(); this.layout = "absolute"; return; }// end function public function justATest() : void { var _loc_1:* = new HTTPService(); t = 2; return; }// end function public function set textarea1(removeAllChildren:TextArea) : void { var _loc_2:* = this._1035784137textarea1; if (_loc_2 !== removeAllChildren) { this._1035784137textarea1 = removeAllChildren; this.dispatchEvent(PropertyChangeEvent.createUpdateEvent(this, "textarea1", _loc_2, removeAllChildren)); } return; }// end function override public function initialize() : void { .mx_internal::setDocumentDescriptor(_documentDescriptor_); super.initialize(); return; }// end function function _MyApp_StylesInit() : void { var style:CSSStyleDeclaration; var effects:Array; if (mx_internal::_MyApp_StylesInit_done) { return; } mx_internal::_MyApp_StylesInit_done = true; style = StyleManager.getStyleDeclaration("Button"); if (!style) { style = new CSSStyleDeclaration(); StyleManager.setStyleDeclaration("Button", style, false); } if (style.factory == null) { style.factory = function () : void { this.color = 255; this.fontSize = 14; return; }// end function ; } var _loc_2:* = StyleManager; _loc_2.mx_internal::initProtoChainRoots(); return; }// end function public function get textarea1() : TextArea { return this._1035784137textarea1; }// end function public function ___MyApp_Button1_click(removeAllChildren:MouseEvent) : void { textarea1.text = "Hello World"; return; }// end function } }
编译结果:MyButton(清单6)
package { import https://varnow.org/files/mx.controls.*; public class MyButton extends Button { public function MyButton() { return; }// end function override public function initialize() : void { super.initialize(); return; }// end function } }
从清单5中可以看到,MyAppAS.as中的import语句已经被合并到该类里了(第9行),而且值得一提的是,如果在MyAppAS.as中仅仅import但是不真正使用的话在编译后的文件中是不会真正import的。而在MyAppAS.as中定义的变量则作为类成员出现在第14行。MyAppAS.as中的函数出现在第48行,只是内部的变量名作了替换。而样式文件中的内容则是在函数_MyApp_StylesInit中进行设置的。此外还可以看出,在MXML标签中的事件属性中直接定义的操作编译后被分离出来作为单独的函数存在了。
Event
ActionScript中的事件机制与浏览器DOM中的事件机制是相似的,它是基于DOM Level3 事件模型实现的,但并没有完全遵循该模型。
事件流(Event Flow)
在浏览器DOM的事件模型中,事件流分为两个阶段:捕获阶段(Capture Phase)和冒泡阶段(Bubble Phase),但是在Flex的事件模型中新增了一个阶段-目标阶段(Targeting Phase)。
- 捕获阶段-这个阶段的结点由从根节点到触发事件的结点的父节点之间的所有结点组成。当事件触发时,会从最顶部的结点开始一直检查到触发结点的父结点为止,如果某个结点上注册了该事件的监听器,那么就会触发相应的事件处理函数。注意这个阶段并不包括触发结点本身。
- 目标阶段-这个阶段的结点仅仅由触发事件的结点组成。
- 冒泡-这个的结点组成与捕获阶段相同,只是触发过程刚好相反,最先从触发结点的父结点开始。
此外,与浏览器DOM的不同还包括:
- 可以给事件监听器定义优先级,通过addEventListener的第4个参数(整数)来指定,数字越大表示优先级越高。在完全使用addEventListener来注册事件监听器的情况下,事件触发的顺序是与注册顺序一致的,但是如果与直接在MXML的事件标签中定义混合使用的话,那么触发顺序是不确定的,因此在这种情况下可以使用优先级来确保事件执行的先后顺序。
- Flex事件机制中,可以通过代码手动的来触发各种类型的事件。
- 每个事件都包含一个target属性和一个currentTarget属性,其中target属性表示触发事件的结点,而currentTarget表示响应事件的当前结点。
You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.