在Flash MX 2004时代,组件体系相当庞大.核心有UIObject,UIComponent,管理器有 StyleManager,DepthManager,FocusManager,为了广播事件还有 EventDispatcher,UIEventDispatcher,除了这些还有n多辅助类,皮肤类等等等等.
到了Flash CS3,得益于新的类结构和全新的深度管理,组件体系得到极大的精简,核心类仅有四个:UIComponent,StyleMananger,FocusManager,InvalidationType,至多再加上三个FocusManager的接口.
1.Flash CS3组件的层次结构
见下图:
从图中可以看出,Flash CS3中的组件分类更加简单明了,组件结构也更加清晰.
下面介绍一下各个组件基类的大体功能,这会方便与选择组件的基类
1.UIComponent
UIComponent 类是所有组件的基类,实现了组件的基本功能,它大概相当于v2版组件中的UIComponent类.它包括处理焦点,键盘事件,鼠标事件,样式管理,组件无效,组件基本事件,输入法设置以及基本的屏幕支持.
2.BaseScrollPane
UIComponent 类处理基本的滚动窗格功能,包括事件、样式、绘制遮罩和背景、滚动条的布局,以及滚动位置的处理.
3.SelectableList
SelectableList 类是所有基于列表的组件(例如 List,TileList,DataGrid 和 ComboBox 组件)的基类,实现了基本的数据提供者,滚动列表,有关列表的事件,属性,方法.
4.BaseButton
BaseButton 类是所有按钮组件的基类,用于定义所有按钮共有的属性和方法.此类仅仅实现改变按钮状态,按钮事件及样式.
5.LabelButton
LabelButton 类添加了标签,图标及切换功能.
6.ScrollBar
ScrollBar 类是一个滚动条,有横,竖两个方向,实现了滚动事件,样式,方法.
2.Flash CS3组件体系的实现与运作方式
(1)深度管理
因为AS3有了新的深度管理,所以在组件中并不需要进行深度管理.
(2)样式管理
由于v2版组件样式管理效率不佳,广为诟病,所以在Flash CS3中采用了一种全新的样式管理方式.
先说说为什么v2版组件效率不佳.在v2版组件中,当样式发生改变时,UIObject类从_root开始递归,将fla文件中的每个MovieClip都访问一遍,所以样式改变的速度极慢,而且存在递归256级的限制.
在Flash CS3中,StyleManager会将每个组件实例都缓存在自己的变量中(这多亏了新的Dictionary类),然后在改变样式时直接设置组件,不存 在多余的循环,使得效率大大增加,所以完全可以在运行时改变样式而不必担心Flash Player因此"崩溃".
再有是每个类都有一个默认的样式列表(可以通过类方法getStyleDefinition()访问),组件实例的所有未设定的样式都会直接读取默认的样式列表,只有被用户更改了的样式才会存储于实例变量中,尽量减少了内存的占用.
样式管理是由UIComponent和StyleManager共同完成的,我们在定义组件时不应直接访问存储了样式的变量,而是使用UIComponent提供的样式方法来获取样式.
UIComponent提供的有关获取样式的方法:
public function getStyle(style:String):Object//获取指定样式的值,这个值是存储与实例上的样式,默认情况下都是null
protected function getStyleValue(name:String):Object//获取指定样式的值,这是为制作组件准备的,获得的值是真实值,即如果实例上没有设定值,则会返回该类的默认样式值.在组件中,我们使用的是这个方法.
protected function copyStylesToChild(child:UIComponent,styleMap:Object):void//将自身的样式复制到子元件 上.如果你在组件中嵌套使用了其它组件,则可以用这个方法复制样式,显然比自己写一个方法要好得多.child是子组件,styleMap是一个样式映射 表,就是将自己的样式映射为子组件的样式,即类似于 {childStyle1:"MyStyle1",childStyle2:"MyStyle2",childStyle3:"MyStyle3"}
(3)焦点管理
首先,UIComponent会自动为stage创建一个FocusManager,但是,问题是UIComponent并 没有使用try...catch...final,也就是说CS3版组件只运行在单一的swf中,如果你要载入一个使用了组件的swf,要确保已经允许载 入的swf访问stage,否则会产生错误,使所有组件都无法运行.
对于焦点管理的细节,我们无需了解,只要知道FocusManager根据组件实现的接口来决定焦点管理的方式.
IFocusManagerComponent:表明这是一个可以接受FocusManager焦点管理的类.
//指示是否可以从FocusManager获取焦点
function get focusEnabled():Boolean;
function set focusEnabled(value:Boolean):void;
//指示是否可以通过鼠标点击获取焦点
function get mouseFocusEnabled():Boolean;
//指示是否可以通过键盘获取焦点
function get tabEnabled():Boolean;
//通过键盘获取焦点的顺序
function get tabIndex():int;
//自己已经获取了焦点,在这个函数中显示焦点框
function setFocus():void;
//绘制焦点框,draw参数指明是否获取了焦点
function drawFocus(draw:Boolean):void;
注 意:事实上UIComponent已经实现了IFocusManagerComponent的全部方法,但并没有实现 IFocusManagerComponent接口,这意味着:如果你要进行焦点管理,要在子类上实现IFocusManagerComponent接口 (尽管不需要额外的方法),如果不实现该接口,则意味着不需要焦点管理.
FocusManager不对组件 tabEnabled、tabChildren 和 mouseFocusEnabled 属性的变化进行监视,所以改变这些属性后,需要将组件从新添加到显示列表(addChild),才能使FocusManager更新这些属性
IFocusManagerGroup:表明几个同样的类组成了一个整体,作为焦点的对象,就像几个RadioButton一样.
//作为一个整体(group)的名字
function get groupName():String;
function set groupName(value:String):void;
//指示自己是否被选择了
function get selected():Boolean;
function set selected(value:Boolean):void;
这个类是为类似RadioButton的组件专用的.
IFocusManager:这个接口是用来自定义一个FocusManager的,在这里不做过多介绍
只要继承里UIComponent,你几乎不必考虑焦点管理的问题,因为UIComponent会帮你处理焦点.如果你需要更高级的焦点管理,则可以监听FocusEvent.FOCUS_IN和FocusEvent.FOCUS_OUT事件.
3.UIComponent类的实现以及如何继承UIComponent类
既然UIComponent类是组件的基类,那么我们要知道该如何继承UIComponent类.
(1)在类的构造函数中,初始化你的变量,然后记得调用super(),让UIComponent来完成剩下的工作.
(2) 使用override protected function configUI():void覆盖configUI()方法.在这个方法中,你要完成组件UI的设置工作.这个方法仅仅在组件初始化时调用一次,所以把 所有只需要执行一次的方法(比如新建一个Sprite之类的)放在这里执行.最后,还是记得调用super.configUI()
(3)然后是组 件的核心方法:draw方法.每当组件的样式(InvalidationType.STYLES),大小 (InvalidationType.SIZE),渲染器(InvalidationType.RENDERER_STYLES),状态 (InvalidationType.STATE),数据(InvalidationType.DATA),滚动 (InvalidationType.SCROLL),选择(InvalidationType.SELECTED)或是所有 (InvalidationType.ALL)发生改变时,都会调用draw方法来重新绘制组件.当然,每次重新绘制所有太浪费了,所以Adobe提供了 InvalidationType类,用常量来标记组件所发生的改变.
首先,在组件发生改变后调用 invalidate(invalidationStyles),invalidationStyles就是InvalidationType类的某个常 量,UIComponent就会记录下发生的更改,然后在draw函数中,用isInvalid(...properties)来检查更改,并根据结果来 绘制不同的部分.
例如调用invalidate(InvalidationType.STYLES),就是表明组件的样式发生了更改.
isInvalid(InvalidationType.SIZE,InvalidationType.STYLES),就是说当SIZE或STYLES发生了更改时返回true.
最后,还是记得调用super.draw().
注意:UIComponent在最开始时已经调用了invalidate(InvalidationType.ALL),来重绘所有部分,无需再手动调用.
(4)组件的延迟处理技术
我 们为什么要使用invalidate()而不是直接调用draw()?答案是为了效率.这是从v2版就开始使用的一项技术,作用是尽量晚的调用 draw(),使用户在同一时间做的许多更改可以在一次draw()内被处理,而不是调用多次draw()来浪费资源.在CS3中使用的是监听stage 的Event.RENDER事件,就是先将要调用的方法用callLater(fn:Function)方法缓存起来,然后在stage重绘时一起调用. 由于使用Event.RENDER事件,而不是以前的EnterFrame.这就带来了一些问题:子组件可能不会及时的得到处理,这时我们只能手动的调用 drawNow(),强制重绘.
下面,一起重新回顾一下组件类执行的过程:
1.构造函数,初始化变量
2.构造函数,调用super()
3.自动执行configUI(),设置组件UI
4.调用super.configUI()
5.由UIComponent调用invalidate(InvalidationType.ALL),等待组件被绘制
6.经过一段时间,draw()被调用,在这里检查组件发生更改的部分,并且绘制更改的部分
7.调用super.draw()
8.当用户发生了交互,或者用AS设置了组件,调用invalidate()方法
重复6 - 8
4.UIComponent类鲜为人知的秘密:)
(其实就是一些protevted的函数而已)
因为UIComponent重写了x,y,scaleX和scaleY,所以我们无法获得正确的scaleX和scaleY,而这两个函数就是干这个的
setScaleY()
getScaleY()
setScaleX()
getScaleX()
设置输入法的状态(是否可用)
setIMEMode(enabled:Boolean)
检查是否处于实时预览状态,如果是的话,会返回true,是一个比较好用的方法
checkLivePreview():Boolean
//检查发生更改的部分
isInvalid(...properties):Boolean
我 最喜欢的函数:从样式中返回DisplayObject,把作为皮肤的样式传入这个函数,如果样式是个类名(Class),它返回这个类的实例;如果样式 是库中的类名(String),它会自动查找类,并返回一个实例;如果样式是个显示对象,就直接返回这个对象.总之,在读取皮肤是很是好用.
getDisplayObjectInstance(skin:Object):DisplayObject
前面已经介绍过了,就不多说了
getStyleValue(name:String):Object
copyStylesToChild(child:UIComponent,styleMap:Object):void
等会调用fn,就是在stage重绘前再调用fn
callLater(fn:Function)
UIComponent中两个抽象方法(就是等待子类去重写的方法),分别是键按下和释放时的监听器
protected function keyDownHandler(event:KeyboardEvent):void {
// You must override this function if your component accepts focus
}
protected function keyUpHandler(event:KeyboardEvent):void {
// You must override this function if your component accepts focus
}
上面说了一大堆,只是用来参考的,我们会用实例来真正了解该如何创建组件:)