
翻译:xfykzz
本篇将介绍V2组件架构,并会讨论组件开发者十分熟悉的此体系下的一些概念.当在Flash MX 2004下创建组件时,您可以选择在MM的组件体系(V2 体系)下构建,或者使用传统的影片剪辑类(MovieClip class).如果您要确保您创建的组件与MM自身的组件有很好的兼容性,那么在V2框架下创建组件和基础类将会变得十分重要.使用V2基础类的另一个好处是MM已经花了很大精力在建立一套省时的组件体系上.在创建组件时常出现的小问题在该体系中也被考虑到了,它使得您能专注于组件的功能上.对于新的开发者来说,他们会发现在V2组件体系下工作会比在MX下直接操作影片剪辑要自然得多.
注意:本篇文章是在build 2.0.0.377组件架构下写的.查看一下您的组件架构版本是十分重要的.如果您碰巧在一个新版架构下工作的话您会发现有一些变化.这个版本(7.0.0和7.0.1)在Flash MX 2004 下是可行的.
组件体系的基础类:UIObject和UIComponent
UIObject类是MovieClip类的子类.它包括该框架的大多数功能.UIComponent是UIObject类的子类.UIComponent是一个我们创建组件时的常用类.
本篇将从介绍UIObject类开始,然后讨论UIComponent类.对UIObject有一定的了解将会对UIComponent类和V2组件体系有很大帮助,因为UIComponent类的应用很大部分都是继承自UIObject类.
I.安装与应用核心
UIObject是MovieClip的子类,所以,您首先得在库中建一个影片剪辑元件.一般地,如果您创建一个与类有关的MovieClip,您创建它并在库中添加关联.如果您的组件继承自UIObject,您需要做一些额外的工作来确保所有的架框包含于您的组件中.您能在这里找到关于设置组件的详细资料.
我们将从一个最基本的组件类的结构开始,该类将与您的库中元件相关联.
import mx.core.UIObject;
class com.rewindlife.controls.TextBox extends UIObject
{
};
说明:如果您想在组件中使用元数据标签,您得注意"import mx.core.UIObject"这一句.理论上来说,您应该用类的全名来做为关联.然而,如果您能正确地使用元数据标签的话,在Flash MX 2004环境下允件您导入您扩展的类,因为如此,将父类一块儿导入更易行一些.元数据标签通常包含了组件的功能.本篇文章将不会讨论元数据,您可以在这里获得更多信息
a.必要属性
当将UIObject作为基本类使用时,您的组件必须声明并给属性赋值.
元件名称
"元件名称"属性应该设置得和该元件在库中的链接ID名称一样.该属性将使createClassObject()方法在运行时从库中找到该组件并创建一个实例到舞台上.如果您的影片剪辑组件在库中的链接ID名称为"YourComponent",那么该属性名称就应设为"YourComponent".

public static var symbolName:String = "com.rewindlife.controls.TextBox";
说明:对于链接ID的一点建议.MM通过链接ID来标识组件.尽管如此,如果您仍按习惯命名的话,其名称很可能会和库中其它名称相冲突.在新版Flash中,组件开发人员通常会在链接ID上加上自定义的前缀来相互区别.在2004中您当然可以这样.例如,您可以使用"usButton"或"com.ultrashock.controls.Button"作为您按钮组件的名称.我推荐使用类名全称的方法,虽然这完全出于个人爱好.如果您想自己的组件能与MM的组件相容性很好地话,我反对直接用"Button"做为链接ID,而且该名称已被MM的按钮组件使用了.未来MM很可能会建议使用完全打包过的方法.
元件源
"元件源"属性也会被creatClassObject()方法使用,特别是当该组件是动态创建的.组件框架使用Object.
registerClass()使得元件源与组件相关联.您得把类名全称赋给它.
public static var symbolOwner:Object = com.rewindlife.controls.TextBox;类名
"类名"属性不是必须的,但通常您应该赋值给它.该属性会被getStyle()方法调用.如果像这样设置"_global.[
className]",getStyle()能够识别并返回适当的值.很多时候该属性值是不受限制的类名,尽管您应该使用统一的标识符来保证不受冲突.例如,如果您创建了一个按钮组件,您应该保证类名的统一,比如用"myButton"来作类名.
private var className:String = "YourComponent";
当组件初始化时,如果初始化尚未完成,在Flash Player 6环境下任何关于该组件的调用将可能会工作异常.在Flash Player 7下您就不用担心了.
/*
Updated code with the required properties and packed into com.rewindlife.controls
*/
import mx.core.UIObject;
class com.rewindlife.controls.TextBox extends UIObject
{
public static var symbolName:String = "com.rewindlife.controls.TextBox";
public static var symbolOwner:Object = com.rewindlife.controls.TextBox;
private var className:String = "TextBox";
//You specify each setter property
private var clipParameters:Object = {someSetter:1,someOtherSetter:1};
//Make sure to include the static call to mergeClipParameters(). What this function
//does is merge the clipParameters of two classes (components)
//You pass it a reference to this class’s clipParameters, and the parent class’s
//clipParameters.
private static var mergedClipParameters :Boolean =
UIObject.mergeClipParameters(TextBox.prototype.clipParameters,
UIObject.prototype.clipParameters);
};
事实上您不用了解过多的细节,组件体系会考虑周到的.如果您对初始化过程中到底发生了什么感兴趣,请继续.
想要清楚下面的代码为什么必须,它有什么用,首先得清楚问题的关键.在Flash Player 6中动态实例化影片剪辑会导致该问题的发生.在Flash Player 6中如果您使用createClassObject()或attachMovie()方法实例化一个组件并得到一个初始化的元件(initObject)的属性值,该元件的设置属性(setter properties)不会被调用的.因为如果该元件包含一些设置属性值.当设置函数可用时,实例的属性值就被清除掉了.这使得属性值丢失.
createClassObject(mx.controls.Button,"myButton",1,{label:"Hello World"});
//the label setter property will never be called
该问题在Flash Player 7中得到解决,但很多时候我们仍会发布为Flash Player 6,这时组件体系就给了我们一个解决方案.
解决方法就是暂时把那些属性值存储起来(clipParameters).它在调用可能失败时(比如当您调用super.init())通知组件体系.因为您有该设置属性的基类,您就能调用mergeClipParameters()方法来把当前的元件参数值用基类收集起来.因为那些属性先于设置函数(stter)可用时被初始化,组件体系就能把值储存起来.最后,当设置函数可用后,组件体系再自动地释放那些值并赋给实例.
b.初始化,必要方法和一些理论
初始化实例时会有很多问题发生,了解一下初始化过程对于您创建高效的组件是很重要的.
从以前的文章中您应该记得您的组件应该包含init(),createChildren(),draw()方法.您不了解的是这些方法是如何被自动调用的.当一个类继承自UIObject类,在这个组件实例化时那些方法就被调用了.您需要了解它们的调用次序及各自功能.
组件初始次序
I.init()方法.
II.createChildren()方法.
III.跳过一帧,这一点儿得时刻牢记.
IV.draw()方法.
I.init()
init方法通常是您使用组件时第一个调用的方法.在init()中您须调用super.init().它将调用UIObject的init()来设置组件架构.该方法只在初始时调用一次.该方法的另一用途是创建成员变量.所有从组件中实例化的变量都得这样做.影片剪辑和文本框(子对象)不在init()中实例化.
II.createChildren()
您将创建设定所有的子对象(影片剪辑子类)在此方法中.子对象与其它组件,影片剪辑,或者文本杠一样是被嵌套绘制的对象.
在适当的时候使用createClassObject(),createLabel()创建子对象是最好的.当然您可以使用attachMovie()和createTextField(),但是建议使用组件架构提供的方法.createClassObject()能很好地通过类名与组件关联,而createLabel()能很好地创建一个可以自动继承您组件的样式的文本框的方法.您将在本篇了解到关于createClassObject()与createLabel()更多的信息.在这个方法里您还可以初始化一些在组件中不会被更改的变量(常量).该方法只在初始化时被调用.
III.Invalidation(失效)
Invalidation是组件体系中最重要的特性之一.组件最初是无效地,然后转到draw()使之生效.您会奇怪为什么我们要这样做,有什么好处.最好的解释就是看个例子.假想一下一位使用者在同一帧设置了很多属性,调用了很多方法,每一个属性或方法都得调用draw()方法来确保组件能及时更新.这会使得draw()方法在同一帧被调用很多次.在Flash中每一帧只翻译代码一次,所以没有必要在同一帧更新数次.所以当所有属性方法都被完成后再调用draw()会更好.而且如果不这样,很可能会引起显示上的问题.这就是invalidate要解决的问题.每个属性和方法储存好变量值,然后在单独一帧调用invalidate(),组件体系会加强所有调用,并在下一帧中调用draw().这样使得整个过程更行之有效.然后draw()取出先前储存的值(一般与模板相关联),更新观察状态.invalidate()方法对所有UIObject类的子类都有效.
您最好记住invalidation()有时是由其它事件所引发的.拿样式来举例,组件体系为我们做了一切.每当设置一个样式时,组件体系就会自动调用invalidation使组件失效,然后调用draw()来重新更新.
没有invalidation时

有invalidation时
了解了这些,您应该知道不要直接调用draw(),而应只调用invalidation().有时您希望组件立即重绘,那么您可以调用redraw(true)来完成.这会使draw()立即调用.当然也许您仍会奇怪为什么当您需要立即更新时仍然不能直接调用draw(),而调用redraw(true).我得承认我把您带入了一个细节问题.invalidation的过程需要在draw()被调用时将invlidationFlag属性值设为true,而且在draw()后事件将被分派到各监听器.这些都得用invalidation()或redraw().不久我们就能看到其它事件的应用,您可以查阅UIObject类中的redraw()方法来了解更多细节.
IV.draw()
draw方法在更新您的组件观察状态时有重要应用.从上面讨论可知,我们得知当组件在invalidation/redrawn时调用draw().您必须清楚该方法是在组件初始化过程中最后调用的方法,也是在组件状态更改时调用的方法.因此您得确保该方法时刻有效并不受干扰.
其它重要的使用方法
size()
尽管该方法在实例化时没有被调用,您仍得注意它.size方法在setSize()方法执行时隐式地执行.size()只处理组件的大小.这确保了在绘制与大小两个方法分开时组件代码能更高效地执行.如果您创建了一个逻辑结构不高的组件,您更希望size()方法放在draw()里,这样在调用invalidation时就自动执行了.这没错,但您的组件将更为复杂.您得调用invalidation()来调用redraw()重绘,或者仅仅是执行一个size().最后可能您将不想再调用draw()中的size(),因为这可能会导致无限的帧循环.
在size方法中您有_width和_height两个参数,它们将大的组件尺寸.当大小需要更改时就得用到这两个属性了.当然,您也可以调用olbWidth和oldHeight两个属性来得到最初的宽高值.
III.公用API
使用组件的一个好处就是减少工作量.目前我们却还没看到这一点,所以接下我们要讨论的就是这个.
a.Methods
move(x,y)
move()方法接收x,y两个参数,然后将当前组件移动目标位置(x,y).在Flash MX 2004中该方法比直接操作_x,_y属性要好.
setSize(width,height)
您也许还记得rize()方法.setSize()事实上就是隐式地调用了size()方法.在调用size()方法前应该先设好_width,_height,oldWidth,oldHeight值.您得记住size()能正确地在运行时改变组件尺寸,除非setSize()方法不能正常工作,否则您不应该越过不用该方法.
b.只读属性
UIObject中有很多只读属性可供使用.这些只读属性对使用者和组件容器会有很大帮助.了解它们是必需的,尽管有时您并没察觉它们正在被使用.
属性:
* right;
* left;
* top;
* botton;
* x;
* y;
c.可见性属性
可见性属性允许使用者隐藏/显示一个组件.了解一下这个"_visible"属性是好的,它会在被调用时发出"reveal(显示)"或"hide(隐藏)"事件.如果您想自己控制可见性,您可以不必理会setVisible()方法.
scaleX/scaleY
该属性与影片剪辑里的_xscale/_yscale属性用法相同.
III.可用方法
可用方法能使您的组件工作变得更轻松.如果您以前使用过这些方法,您大可浏览一下即可.
cancelAllDoLaters()
cancelAllDoLaters()删除队列中的函数调用,所有doLater()调用将被清除.查看一下doLater()方法的信息可以了解更多.
doLater(obj:Object,function:String)
doLater()方法是最常用的将一个方法推迟到下一帧执行的方法.当调用该方法时,通过参数,该方法将目标函数储存在一个数组中,然后在下一帧调用.如果有多个函数,那么它们将一块儿在下一帧被调用.记住当一个组件无效时,在数组中的方法会被自动调用.然而,如果您调用redraw(),那些方法将被立即调用.如果不是上述情况,它们将在下一帧被调用.当您不得不在推迟一个方法调用前使用onEnterFrame,这个方法就非常有用了.例如:doLater(this,"setSomething")将会使得当前组件的"setSomething"方法在下一帧执行.
createLabel(name:String,depth:Number,text:String)
该方法创建一个文本框对象,并取得创建该文本框对象的类的样式值.这是推荐的创建文本框的方法.
getTextExtent2(text:String)
比getTextExtent()方法更好用的方法,它可以将TextFormat类原型在运行时添加进去.用法与getTextExtent()相同.
drawRect(x1:Number,y1:Number,x2:Number,y2:Number)
根据四点绘制矩形
setSkin()
当您使用组件换肤功能时调用.本篇我们将不讨论关于换肤的过多细节.
getStyle(styleProp:String)
getStyle()方法返回一个样式的确定值.例如:getStyle("color"),将会返回正在使用的颜色值.如果以前您使用过组件的样式功能,您很可能知道在实例或类前设置全局样式,getStyle()方法能返回正确位置的值.如果您之前没有设定样式,即是默认状态,该方法将返回undefined.恰当地使用getStyle(),使您可以能正确地判断当前状况.
setStyle(styleProp:String,value)
如果您的组件使用自定义外观,那您肯定会用到该方法.该项方法使组件采用自定义样式.
IV.事件
新组件体系的另一新特点就是事件系统(mx.events.* 包).不仅仅是MM创建了新的事件系统,而且还创造了很多您组件支持的事件.当您的组件继承自UIComponent类时,
它就自动获得这些事件了.
继承自UIObject的事件
* resize:当setSize()被调用时发生该事件.
* move:当调用move()时发生该事件.
* draw:每一次draw()方法调用时发生该事件.通常是当组件被绘制时或组件无效时该事件发生.
* reveal:当使用者将组件属性visible=true时发生该事件.
* load:当组件加载时发生该事件.在影片剪辑调用时被隐式执行.
* unload:当组件卸载时发生该事件.
这只是开始,您还可以发布自定义事件
自定义事件
使您的组件包括一些自定义事件是一个不错的想法,这将使使用者更方便地使用组件.我建议您在一开始创建组件时就花时间考虑一下应该设定哪些自定义事件,因为设定自定义事件本身十分简单.这也是使用MM的组件体系另一大好处.想想这是多么可怕,如果每一个组件您都不得不建立自己的方法.在本篇中我们不再讨论这些方法的公用API,如果您之前使用过V2组件体系的话,您一定不会陌生.基本的,您的组件要支持addEventListener()和removeListener().
UIEvent发布机中的Mix-in
如果您看看UIObject类的话,您会发现许多事件应用.原因是它通过mix-ins应用.Mix-ins是向类中添加功能的一种方式.这是由AS结构松散性所决定的.如果您看一看UIObject类,您只能在一些如dispatchEvent(清除广播事件),handleEvent(处理事件),removeEventListener(清除事件监听器),和addEventListener(添加事件监听器)之类的无函数应用的声明中找到事件系统的踪迹.向对象添加事件系统的方法是为UI项调用mx.events.UIEventDispatcher.initialize(关于此对象的).这些方法不在UIObject中,而在UIObject扩展中(mx.core.ext.UIObjectExtensions),然后在初始化外观时被调用.当您发现组件不接受EventDispatcher的指示时,了解这点是很有帮助的.
dispatchEvent(eventObj:Object)方法
自定义事件时会用到dispatchEvent()方法.调用此方法时,所有监听器会被通知.事件对象将通知当前监听器,及所有的监听器.一般地需要创建一个有着与该事件同类型属性的匿名对象.例如:dispatchEvent({type:"change"})将通知所有change事件监听器.事件对象也包括一个目标属性.如果您的事件对象没有一个目标属性,那默认会与当前组件关联.您也可以发送其它属性,就像这样:dispatchEvent({type:"change",oldValue:someValue}).这会在调用dispatchEvent()时给所有监听器一个附加属性"oldValue".
UIComponent基础类
现在您已经完全了解了UIObject类.尽管UIObject类提供了很多功能,您还必须了解另一个基础类.UIComponent类是UIObject类的子类,因为几乎提到的所有UIObject类的信息都对UIComponent适用.UIComponent类在焦点处理,激活属性,用户交互和智能改变尺寸上更有用.这也是余下的篇幅我们将介绍的.
许多附加功能组件体系已经自动为我们做了.除非您要求组件完成一些架构自身无法处理的更为复杂的情况,否则您不需要对焦点处理进行额外的操作.用户交互也与焦点处理类似,通常架构自己就能处理了.
需要注意的一些变化
请确定tabEnabled属性值被设为true,这在UIObject中可没有这样要求.这样就能使组件能接受焦点,这也是UIComponent支持焦点处理的原因之一.
更智能地改变尺寸
在UIObject中,我们提到了rize()方法.UIObject类在设计中默认使用resize()来适应scaleX和scaleY属性值来确保组件始终与边界矩形框一样大小.在UIComponent中不是这样处理了.它使用比简单地适应scaleX/scaleY更智能地尺寸计算法则来调用size()方法进行处理.
公用API的附加功能
属性
除了使用UIObject的属性以外,还有一些UIComponent类增加的额外属性.
enabled
enabled属性使组件可用/不可用.它是一个布尔属性.当组件无效(invalidated)时,该值在draw()中可用.在draw()中您得明确现在enabled属性值是什么.
private function draw():Void
{
if(enabled)
{
//make sure the component looks enabled
}
else
{
//make sure the component looks disabled
}
}
tabIndex
tabIndex属性是在组件接受到Tab排序后供焦点管理器使用.
方法
getFocus()
通过焦点管理器找到当前焦点所在对象.
setFocus()
为类设定焦点.您可以通过默认调用Selection.setFocus(this)来忽略此方法.
实用方法
getFocusManager()
焦点处理器对V2组件中所有焦点进行处理.在V2之前,我们严重依赖播放器处理焦点问题.因为内建的播放器焦点处理机制不能很好地处理具体的需要,焦点管理器就应运而生了.大部分时间里您不需要对焦点管理器做些什么.您可以在MM的帮助文档中找到关于焦点管理器的更多资料.getFocusManager()是一个很便捷的与焦点管理器互通的方法.
事件
1. focusIn:当组件接收焦点时发生该事件
2. focusOut:当组件失去焦点时发生该事件
3. keyDown:某一按键被按下,当前获得焦点的组件即发生该事件
4. keyUp:某一按键松开,当前获得焦点的组件发生该事件
结论
本篇文章介绍了许多了.希望这能使您对V2组件体系的了解有一定帮助,并能很好地使用它们.虽然说了这么多,但是把各个方面都涉及到也是不可能的.我们希望在以后的文章中介绍一下组件的换肤,高级焦点处理和组件的数据处理.
原文地址:http://www.ultrashock.com/tutorials/flashmx2004/v2a-01.php
[参加讨论]
(完)
编辑:闪客帝国