最近在看《Flex企业应用开发实战》这本书,里边内容写得相当好,以笔记的形式记录下来和大家分享。(如有错误,请及时提出,不胜感激)如需要源码或者PDF格式文件可以给我留言或者发邮件。
第一章 Flex企业应用开发入门
1.1 B/S企业应用开发入门
以前WEB开发那些很恶心的东西,不详述
1.2 噩梦结束,新时代来临
Flex来了,还讲了作者的一个项目的实际应用。
1.3 Flex概述
1.Flex是由Adobe在Flash平台上,用AS开发的一套RIA开发套件及其集成开发环境
2.Flex SDK由Flex框架类库、Flex编译器、调试器、MXML和ActionScript语言及其他工具组成,Flash Builder是其集成开发环境。
3.开发者可以利用Flash Builder通过拖拽方式开发人机界面,用ActionScript语言编写代码,调用Flash及Flex SDK提供的API进行RIA开发,然后利用Flash Builder进行编译,编译后的产品可以再Flash虚拟机中运行。
4.经过编译后的Flex应用及其组件库是运行在Flash虚拟机中的SWF文件。SWF文件有一系列的有顺序的“帧”组成,“帧”中包含编译后的ActionScript字节码以及所需的数据。
1.4 Flash Play工作模型
Flash Player由ActionScript虚拟机(AVM)和图形渲染引擎(GR)组成。AVM负责执行编译后的ActionScript字节码;图形渲染引擎则用来绘制显示列表(Display List)中的图形对象。
1.4.1 Flash Player的帧执行模型
1.Flash Player发出事件。事件包括Timer、Mouse、ENTER_FRAMES、URLLoader发出的事件。
2.用户代码被执行。执行第1步中Flash Player发出的事件的代码。这里的用户代码主要指AVM意外的由开发人员编写的代码,包括Flex SDK中的代码。
3.RENDER事件被派发。RENDER事件由用户代码中调用flash.display.Stage对象的invalidate()方法引起的。Stage是Flash显示对象的根容器。Invalidate()方法只是告知Flash Player当前显示列表已经发生变化或者已经失效,该方法并不直接派发RENDER事件,而是由Flash Player渲染之前检查显示列表是否有效。如果显示列表失效,则会在渲染新的显示列表钱派发RENDER事件,给用户代码最后的执行机会。
4.最后的用户代码执行。在这里再次调用invalidate()方法将不会引起派发RENDER事件。
下面的代码显示了invalidate的用途。
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
layout="absolute"
minWidth="955" minHeight="600"
addedToStage="onAddToStage(event)">
<fx:Declarations>
</fx:Declarations>
<fx:Script>
<![CDATA[
private function onRender(event:Event):void
{
trace("正在渲染");
if(this.stage)
{
this.stage.invalidate();
}
}
private function onEnterFrame(event:Event):void
{
trace("进入下一帧");
/* if(this.stage)
{
this.stage.invalidate();
} */
}
private function onAddToStage(event:Event):void
{
this.stage.frameRate = 1;
this.stage.addEventListener(Event.RENDER,onRender);
this.stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
]]>
</fx:Script>
</mx:Application>
5.Flash Player最后渲染出最新的显示列表。显示列表的过程中不会有用户代码的执行,这是一个黑箱操作,开发者无法参与。
图1-1 Flash Player执行帧中的渲染的图形过程
根据SWF规范,每个SWF文件中都设定了帧的播放速率,即每秒播放的帧数(FPS)。FPS是开发者期望的最大的播放速度,而不是实际的播放速度。Flash Player不会漏掉帧中任何需要执行的代码和任何需要渲染的图形。
有人把Flash Player堪称一个弹性的跑道,执行AS代码和渲染图形是两个执行阶段,正常情况下,Flash Player按照SWF的FPS设置去执行每一帧,当某帧的执行时间和渲染时间之和超过FPS设定的时间,那么执行一帧的时间会超过FPS。下图展示了Flash Player的弹性跑道模型。
图1-2 Flash Player的弹性跑道模型
1.4.2 Flash Player的渲染模型
显示列表是运行期间,Flash图形渲染引擎在屏幕上渲染出“树”状结构的图形对象。只有在显示列表中的对象才能显示在屏幕上。(简单的说,就是不论是容器还是控件,最终的父对象必须是state才能显示在界面上)
Flash Player构造树状显示列表的步骤:
1.Flash在首次加载SWF时会自动创建一个Stage的对象,然后将这个Stage对象添加到显示列表中;
2.查找SWF文件的主类,然后创建SWF文件的主类实例,并经该实例作为Stage对象的第一个子对象添加到显示列表中;
3.SWF加载自己的显示列表的显示对象,从而形成完整的显示列表。
下面是几个要注意的概念。
Stage:所有图形内容的最外层容器,是显示列表的根。每个显示对象都一个stage对象指定所属的Stage。通过Stage可以全局性的控制整个显示区域的属性,比如图形质量、放缩模式和帧频率等。
SWF文件主类:每个SWF都有一个关联的ActionScript类,即是SWF的文件主类。该类必须是Sprite的子类。
显示对象(Display Object):在ActionScript中,应用程序的屏幕上出现的所有的元素都属于“显示对象”类型。下面是显示对象的继承层次关系:
图1-3 显示对象的继承层次关系
显示对象容器:可以包含子级列表的显示对象。
可交互对象(Interactive Object):通过键盘或者鼠标与人进行交互的显示对象的抽象基类。
Sprite:有拖拽功能或者按钮方式工作的特殊的显示对象容器。
MovieClipe:增加了时间轴或者时间轴的控制功能。
1.4.3 Flash中的事件机制
Flash的ActionScript支持事件驱动编程。
Flash中每个事件都由一个事件对象表示。事件对象是flash.events.Event类或者其某个子类的实例。事件对象的作用有如下两个
·将特定事件的信息存储在一组属性中来代表实际事件。
·事件对象包含一组方法,可用于操作事件对象和影响事件处理系统的行为。
派发事件是指将事件按照一定的规则顺序传递给其他对象并执行这些对象上注册的事件的侦听器。
能够接收事件的对象称为事件的目标对象。
侦听器是用于响应特定事件的函数或方法。
1.创建自定义事件
·自定义事件必须从flash.events.Event类继承
package com.suifeng.event
{
import flash.events.Event;
public class TestEvent extends Event
{
public static const TEST_EVENT:String = "TEST_EVENT"; // 事件类型
public var data:Object; // 存储事件的附加信息
public function TestEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
}
·构造函数的参数的意义
type:事件类型
bubbles:事件是否冒泡
cancelable:调度过程中,默认处理是否可以取消
1.派发事件
·flash.events.EventDispatcher类及其子类的实例可以派发事件。
·派发事件的对象称为事件源,也是派发的目标对象之一,因此可以侦听事件。
·构造函数的参数的意义
type:事件类型
bubbles:事件是否冒泡
cancelable:调度过程中,默认处理是否可以取消
1.派发事件
·flash.events.EventDispatcher类及其子类的实例可以派发事件。
·派发事件的对象称为事件源,也是派发的目标对象之一,因此可以侦听事件。
·构造函数的参数的意义
type:事件类型
bubbles:事件是否冒泡
cancelable:调度过程中,默认处理是否可以取消
1.派发事件
·flash.events.EventDispatcher类及其子类的实例可以派发事件。
·派发事件的对象称为事件源,也是派发的目标对象之一,因此可以侦听事件。
·构造函数的参数的意义
type:事件类型
bubbles:事件是否冒泡
cancelable:调度过程中,默认处理是否可以取消
2.派发事件
·flash.events.EventDispatcher类及其子类的实例可以派发事件。
·派发事件的对象称为事件源,也是派发的目标对象之一,因此可以侦听事件。
package com.suifeng.event
{
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
public class TestEventDispatcher extends EventDispatcher
{
public function TestEventDispatcher()
{
super();
}
public function createAndDispatchTestEvent():void
{
// 创建事件
var event:TestEvent = new TestEvent(TestEvent.TEST_EVENT);
// 为事件添加附加信息
event.data = "This is a Test Event";
// 派发事件
this.dispatchEvent(event);
}
}
}
3.目标对象及注册事件
·只有flash.events.EventDispatcher类及其子类才可以成为目标对象,也就是只有flash.events.EventDispatcher类及其子类的实例才可以注册事件。
package com.suifeng.event
{
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
public class TestEventDispatcher extends EventDispatcher
{
public function TestEventDispatcher()
{
super();
this.addEventListener(TestEvent.TEST_EVENT,onTestEvent);
}
public function createAndDispatchTestEvent():void
{
// 创建事件
var event:TestEvent = new TestEvent(TestEvent.TEST_EVENT);
// 为事件添加附加信息
event.data = "This is a Test Event";
// 派发事件
this.dispatchEvent(event);
}
private function onTestEvent(event:TestEvent):void
{
trace(event.data);
}
}
}
使用EventDispatcher的addEventListener方法完成事件侦听器的注册。addEventListener方法定义如下:
public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
type:被侦听事件的类型
listener:事件侦听器
useCapture:指示事件侦听器的工作的阶段。取值为true,说明侦听器仅在捕获阶段工作,取值false说明侦听器只能在目标和冒泡阶段工作,如果在全部阶段都要工作的话,则要注册两次。
priority:指定侦听器的优先级。数值越大,优先级越高,越优先执行。
useWeakReference:对事件侦听器的引用是强引用还是弱引用。弱引用的事件侦听器可以再该侦听器函数所在的对象无任何其他引用时被垃圾回收。
移除事件侦听器使用removeEventLinstener要使用removeEventListener,方法定义如下:
public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
参数含义参考addEventListener的参数。
下面是测试代码
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx" layout="absolute" minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
import com.suifeng.event.TestEventDispatcher;
protected function btnTest_clickHandler(event:MouseEvent):void
{
var dispatcher:TestEventDispatcher = new TestEventDispatcher();
dispatcher.createAndDispatchTestEvent();
}
]]>
</fx:Script>
<mx:Button id="btnTest"
label="测试自定义事件"
click="btnTest_clickHandler(event)"/>
</mx:Application>
4.事件派发规则
·事件源对象是一个不在显示列表中的孤立的对象的时候,事件只能传递给自己
·事件源对象是显示列表中的显示对象的时候,事件的派发顺序是从最顶级的Stage对象开始向下穿行源对象的各级父对象,直到到达事件源对象,如果构造对象时bubbles对象设置为true时,事件会从事件事件源对象开始向上依次穿行各级父显示对象,直至到达Stage对象。
推论:
(1)Stage对象可以接收显示列表中的所有显示对象所派发的事件
(2)Stage对象第一个接收到显示列表中的显示对象派发的事件
·某些类型的事件,如enterFrame和init类型事件,事件直接派发到源对象, 不会参与捕获和冒泡阶段。
注意:事件调用并不是异步的。测试代码如下:
public function createAndDispatchTestEvent():void
{
// 创建事件
var event:TestEvent = new TestEvent(TestEvent.TEST_EVENT);
// 为事件添加附加信息
event.data = "This is a Test Event";
// 派发事件
this.dispatchEvent(event);
trace("当testEvent对象的所有的侦听器都执行完毕后才能执行");
}
5.创建可取消的默认事件处理器
创建默认事件处理器主要是用于防止组件的默认的事件处理器的执行。
创建默认处理器的四个步骤如下
(1)创建事件设置该事件可以被取消,即Event第三个参数设置为true。
(2)在组件内部,添加默认事件侦听器要使用EventPriority.DEFAULT_HANDLER优先级,于是该事件的默认处理器优先级低于普通的事件。
(3)在自定义事件的处理的代码中,要调用event.preventDefault()来阻止默认事件处理器的执行
(4)在组件内部,默认事件处理器代码中使用event.isDefaultPrevented()来检查用户是否阻止了默认事件。
详细代码如下:
package com.suifeng.components
{
import flash.events.Event;
import mx.controls.Alert;
import mx.core.EventPriority;
import mx.core.UIComponent;
[Event(name="alarm",type="flash.events.Event")]
public class MyComponent extends UIComponent
{
public function MyComponent()
{
super();
addEventListener("alarm",handleAlarm,false,EventPriority.DEFAULT_HANDLER,true);
}
public function triggerAlarm():void
{
this.dispatchEvent(new Event("alarm",false,true));
}
private function handleAlarm(event:Event):void
{
if(!event.isDefaultPrevented())
{
Alert.show("事件正常执行");
}
}
}
}
测试代码如下
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx" layout="vertical" minWidth="955" minHeight="600" xmlns:components="com.suifeng.components.*">
<fx:Script>
<![CDATA[
protected function btnTrigger_clickHandler(event:MouseEvent):void
{
alarm.triggerAlarm();
}
protected function alarm_alarmHandler(event:Event):void
{
if(chkPrevent.selected)
{
trace("Default behivour is prevented!!");
event.preventDefault();
}
else
{
trace("We do not prevent the default behivour");
}
}
]]>
</fx:Script>
<mx:Button id="btnTrigger"
label="触发事件"
click="btnTrigger_clickHandler(event)"/>
<mx:CheckBox id="chkPrevent"
label="禁止默认行为?"
selected="true"/>
<components:MyComponent id="alarm"
alarm="alarm_alarmHandler(event)"/>
</mx:Application>