这是一个很好用的技术,我已经在As3中使用它好长一段时间了。使用之前最基本的条件是,你已经知道了类的名字和所属包的的路径,这样就可以轻而易举的在运行时创建类。那么为什么需要这么做呢?有两种情况:一是你有从外部的swf导入的类,二是你已经编译了所有的类,却还想按照XML的配置改变其中的一些类,又不想重新编译。当你用一种CMS生成了你的站点,那么这也是一个非常好用的技术。想象一下,可以把类用于CMS的一段中(a section in the CMS),并且允许编辑从一套“类模板”中选取所需要的。现在,我们有一些在某些地方使用的例子,一起看看我们是如何轻松实现它的。
第一步:创建一个测试类
第一件事情,我们需要做的是创建一个测试类。在你的项目中,创建一个包路径为com.flashartofwar.example的类,如下所示:
- package com.flashartofwar.example {
- import flash.display.Sprite;
- /**
- * @author jessefreeman
- */
- public class RedBox extends Sprite {
- protected var _width:Number = 0;
- protected var _height:Number = 0;
- /**
- * We override the public setter for width so we can redraw
- * the box when the width is changed.
- */
- override public function set width(value : Number) : void {
- _width = value;
- trace("Width", super.width);
- redraw();
- }
- override public function get width() : Number {
- return _width;
- }
- /**
- * We override the public setter for width so we can redraw
- * the box when the width is changed.
- */
- override public function set height(value : Number) : void {
- _height = value;
- redraw();
- }
- override public function get height() : Number {
- return _height;
- }
- public function RedBox() {
- width = 100;
- height = 100;
- redraw();
- }
- /**
- * This simply clears the graphics and redraws the box based
- * on the new width and height.
- */
- public function redraw():void
- {
- graphics.clear();
- graphics.beginFill(0xFF0000);
- graphics.drawRect(0, 0, width, height);
- graphics.endFill();
- }
- }
- }
就像你看到的,这个基本的类继承了Sprite,并添加了管理width/height的自定义逻辑,还画了一个shape。我不愿将更多的时间花费在这个类上,它只是一个最基本的例子,任何的类都可以在这里良好的运行。
第2步:建立一个Doc类
现在我们需要一个文档类,来测试我们的RedBox类是否工作。
- package {
- import flash.display.StageAlign;
- import flash.display.StageScaleMode;
- import com.flashartofwar.example.RedBox;
- import flash.display.Sprite;
- /**
- * @author jessefreeman
- */
- public class DynamicClassDemo extends Sprite {
- public function DynamicClassDemo() {
- configureStage();
- createRedBox();
- }
- private function configureStage() : void {
- stage.scaleMode = StageScaleMode.NO_SCALE;
- stage.align = StageAlign.TOP_LEFT;
- }
- private function createRedBox() : void {
- var redBox:RedBox = new RedBox();
- redBox.width = 100;
- redBox.height = 100;
- redBox.x = redBox.y = 10;
- addChild(redBox);
- }
- }
- }
同样,这个类也没什么特殊的。我设定了下stage,并建立一个可以创建RedBox的类。如果你运行下,你会看到一个100×100的红色的盒子。到这为止呢,所有的事情看起来都是这么的正确。
第三步:使用getDefinitionByName
接下来,让我们尝试下动态创建RedBox类。我将要使用到一个叫做getDefinitionByName的包方法,它可以通过完整的包路径和类名来查找某个类。把createRedBox方法用以下的代码替换。
- private function createRedBox() : void {
- var tempClass : Class = getDefinitionByName("com.flashartofwar.example.RedBox") as Class;
- var redBox:RedBox = new tempClass();
- redBox.width = 100;
- redBox.height = 100;
- redBox.x = redBox.y = 10;
- addChild(redBox);
- }
你也要导入如下:
- import flash.utils.getDefinitionByName;
好,注意到了吗?我们这里只是简单地用new tempClass()替换了new RedBox(),并在这行之前使用了getDefinitionByName方法。只有这一点改变。我们建立了一个Class类型的持有变量,然后用getDefinitionByName返回了一个com.flashartofwar.example.RedBox类的实例。一旦我们有了一个类自己的引用,那么我们就可以简单地创建新的实例。灵巧的技巧,对吧?同样,我们若不使用As3自己的internal类去到系统里找我们所需要的类,这样写也是可以的,var tempClass:Class =com.flashartofwar.example.RedBox。运行一下,你同样可以看到一个和第2步相同的红色的盒子。
让我们通过XML来创建类吧,你可能会惊讶它有多么简单哦!
第四步:通过XML创建RedBox
现在我们要做与第三步同样的事情,只不过要把类的名字放到XML里。在这里例子里我要做些令人兴奋的
小变化。为什么你不用下面的代码再一次代替createRedBox呢?
- // This is some sample XML data, we can just as easily
- // loda this at runtime instead of compiling it in.
- var xmlData:XML = <boxes base-package="com.flashartofwar.example">
- <box name="box1" class="RedBox" x="10" y="10" width="100" height="100"/>
- <box name="box2" class="RedBox" x="120" y="10" width="50" height="100"/>
- <box name="box3" class="RedBox" x="190" y="10" width="20" height="100"/>
- </boxes>;
- // Pull out some core data we need from the xml
- var basePackage:String = xmlData.@["base-package"];
- var boxes:XMLList = xmlData.*;
- var box:XML;
- var boxInstance:DisplayObject;
- // Loop through each box node and build it's class
- for each(box in boxes)
- {
- // It is very important that we use quotes for the class
- // because it is a reserved word
- var tempClass : Class = getDefinitionByName(basePackage+"."+box.@["class"]) as Class;
- // Here we type box to DisplayObject since we are not
- // sure what class it actually is
- boxInstance = new tempClass();
- boxInstance.x = Number(box.@x);
- boxInstance.y = Number(box.@y);
- boxInstance.width = Number(box.@width);
- boxInstance.height = Number(box.@height);
- addChild(boxInstance);
- }
- }
确定你要导入了下面的代码哦:
- import flash.display.DisplayObject;
这回,我们有很多事情要做了,我添加了一些注释来帮助你。在上面的代码中,我们写了一些xml,并循环每一个box结点。一个好的技巧是,为了减少重复性得写代码,把package名字作为根结点的属性。下一步,你会看到每一个box都有一个名字、一个类、一个x值、一个y值,宽度和高度。最后的事情是,你应该注意我把redBox变量改成boxInstance,并且它现在的类型是DisplayObject类型的。运行下,看看发生了什么。
哦!你会看见如下的错误:
- ReferenceError: Error #1065: Variable RedBox is not defined.
- at global/flash.utils::getDefinitionByName()
- at DynamicClassDemo/createRedBox()
- at DynamicClassDemo()
在没有定义类型之前,它不再是com.flashartofwar.example.RedBox的引用,所以编译器会忽略导入它。这是在通过XML动态创建class的时候经常出的一个问题,我们不得不要使用一些技巧强制编译器包括这个类。让我们在com.flashartofwar.example包中创建一个叫做IncludeClasses 的新类:
- package com.flashartofwar.example {
- /**
- * @author jessefreeman
- */
- public class IncludeClasses {
- RedBox;
- }
- }
我知道,你一定认为这太疯狂了吧,但是这就是解决动态创建类的方法。非常简单地把类名字放进一个空类里,编译器就是加载它,即使在这个类里面没有任何的变量被定义。最后的事情是,定义一个该类的变量放到我们的文档类里。这里是代码:
- // This class forces the compiler to import your dynamic Classes
- var includeClasses:IncludeClasses;
并确定你导入了这个:
- import com.flashartofwar.example.IncludeClasses;
现在再次运行你的项目,你会看到3个盒子通过XML动态得被初始化出来。这可不简单哦,你可以自己试着创建一个类,来创建绿盒子和蓝盒子,然后把它们加入到IncludeClasses类里面,然后再在XML文件中配置一下。他们会像红盒子这样被创建出来。当我们知道包路径和类名的时候,我希望使用getDefinitionByName的这个简单的例子可以让我们更简便地创建类。请在评论里自由的发言吧,并讨论你在自己的项目中是如何使用这个技巧的呢。