FWD: Creating an Executable Apache Pivot App using Maven

old archive url :http://michaelbushe.wordpress.com/2009/12/22/creating-an-executable-apache-pivot-app-using-maven/

Apache Pivot is a very interesting new RIA platform.  You can think of it as a competitor to flex, Silverlight, and JavaFX.  Unlike all three of those languages, it uses Java and XML – two generally familiar languages, to create applications.  It’s structured similar to flex – it has an XML file that describes an application’s layout and allows enough functionality to create simple and not so simple applications with just XML with embedded scripts.  Unlike flex, the “code behind” language is straight up Java.  It’s deployment model is just like an applet’s, so it’s ubiquitous.  When you dig deeper, there are lots of little niceties – things that are simple and make sense.  It’s nice to work with.

This post describes how to set up a maven project to build and deploy an Apache Pivot application.  Once Pivot is in the maven central repo (it’s not quite out of incubator as of this writing), then the result of this post can be made into an archetype and contributed back.  The result of this post is is this GitHub repository commit.  (Git is very nifty – the fact that the entire set of files is available from one Git commit id is, well, wicked, as we say in Massachusetts.)

We’ll steal the code from the Pivot StockTracker tutorial, since it will be extended with a graph and used as an example of how and why to use the EventBus in UIs.  The examples will be posted on the EventBus.org blog soon.   I’ll add a blog entry here when the eventbus.org blog post is available.

Start by using the maven quickstart to create a blank Java project from an archetype:

mvn archetype:create -DgroupId=org.eventbus.tutorial -DartifactId=stocktracker
cd stocktracker
mvn install

To populate the example, we can pull the StockTracker sources to the our package in the src/main/java directory and delete the files we don’t need:

mkdir src/main/java/org/eventbus/tutorials/pivot/stocktracker
cd src/main/java/org/eventbus/tutorials/pivot/stocktracker
svn co http://svn.apache.org/repos/asf/incubator/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/stocktracker/ .
rm -rf .svn
rm App.java
rm ../../../../../../../test/java/org/eventbus/tutorial/AppTest.java

According to Maven conventions, only the java code belongs in this directory, so let’s move the other files to the resource directory so that they can easily be picked up from the jar at runtime.  A better practice may be to make image, json, and wtkx directory, or maybe make a main/wtkx directory for the latter, but let’s keep it simple.  The community may come up with better standards for Pivot development with Maven.

cd ../../../../../..
mkdir resources
mv java/org/eventbus/tutorial/*.json resources/org/eventbus/tutorials/pivot/stocktracker
mv java/org/eventbus/tutorial/*.png resources/org/eventbus/tutorials/pivot/stocktracker
mv java/org/eventbus/tutorial/*.wtkx resources/org/eventbus/tutorials/pivot/stocktracker

Change all the packages in the Java files to the new package.

find . -name *.java -print | xargs sed -i -e 's/org.apache.pivot.tutorials.stocktracker/org.eventbus.tutorials.pivot.stocktracker/g'
find . -name *.wtkx -print | xargs sed -i -e 's/org.apache.pivot.tutorials.stocktracker/org.eventbus.tutorials.pivot.stocktracker/g'

(This is why I keep such detailed blogs.  It took me while to figure out that the -e is required on Mac, but not Linux, and I’m sure I’ll forget again, but I’ll remember that I had to do this before.)

The maven default build uses Java 1.3 source, even though Java 1.5 is about to be obsoleted as of this writing, so we’ll have to tell maven to get with the new decade and use  Java 6  (I think Pivot will work for Java 5 too) by adding this before <dependencies>:

<build>
<sourceDirectory>src</sourceDirectory>
<testSourceDirectory>test/src</testSourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>

Pivot is not in the central maven repo yet, so we have to install it locally, which means building it first.  The Pivot team is rock solid, pulling and building the project is easy and reliable.  See the BUILD file in the svn trunk for details, but on a Mac using Java 6, it’s like this:

cd ~
mkdir pivot
svn co http://svn.apache.org/repos/asf/incubator/pivot/trunk/ .
export CLASSPATH=~/dev/maven-ant-tasks-2.1.0.jar:~/dev/junit/junit-4.8.1.jar:/System/Library/Frameworks/JavaVM.framework/Resources/Deploy.bundle/Contents/Home/lib/plugin.jar:/System/Library/Frameworks/JavaVM.framework/Resources/Deploy.bundle/Contents/Home/lib/javaws.jar
ant maven-install

This builds all the pivot jars using ant and installs them to your local maven repo.

Now go back to our project and add the pivot dependencies to pom.xml:

<dependencies>
<dependency>
<groupId>org.apache.pivot</groupId>
<artifactId>pivot-core</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.apache.pivot</groupId>
<artifactId>pivot-wtk</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.apache.pivot</groupId>
<artifactId>pivot-web</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.apache.pivot</groupId>
<artifactId>pivot-wtk-terra</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.apache.pivot</groupId>
<artifactId>pivot-charts</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>

You might not need charts, but I will for the EventBus stuff coming soon.

Now

mvn install

should lead to a successful build.

It would be real nice to generate an executable jar file that has the entire classpath in it so that launching the app is a simple java -jar command.  To do this add the following as a plugin after the compiler plugin configuration shown above:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>attached</goal>
</goals>
<phase>package</phase>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>org.eventbus.tutorials.pivot.stocktracker.StockTracker</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>

Now run build again and launch the app:


mvn clean install
java -jar target/stocktracker-1.0-SNAPSHOT-jar-with-dependencies.jar

如何获取Flex对象实例的Class

flex应用中有时会反向映射运行中的对象实例的类,比如有一个方法传递的参数为Object,但在方法中要判断该对象是否为要处理的类,有些采用instanceof关键字,还可以采用类型比较的方法:

dynamic class  A {}
trace(A.prototype.constructor);      // class A
trace(A.prototype.constructor == A); // true
var myA:A = new A();
trace(myA.constructor == A);         // true

FDT3.5正式版已经发布

今天下午还在用MyEclipse8中安装FDT 3.5 Beta2,晚上回家再重新安装时,发现FDT3.5已经releases.

 

With version 3.5 of FDT we are introducing great new features for both MXML and ActionScript editor. While we are focused on bringing you the best MXML editor for flex development at this time, we are continually improving FDT.

FDT 3.5  Presentation

The FDT 3.5 Update was recently released. Watch the new features of FDT 3.5 live in action! Michael Plank shows you what’s new in FDT 3.5 and how you can use the great features.

Features and Improvements of FDT 3.5

Full Autocompletion Support in MXML Editor

AS3 Autocompletion in CDATA, quotes and curly braces

Search in MXML Files

Wizard for creating Top Level Variables and Functions

New and improved Code Templates

Autocompletion for MXML Tags

Autocompletion for event types

Watch that feature in action

Improved Quick Outline

Autocompletion for constant convention

Watch that feature in action

Semantic Highlighting and Autocompletion for inline XML

Watch that feature in action

New Quick Fixes

Watch that feature in action

Brand new Debugger

Variable folding and XML Preview are the new features in the debugger


Robotlegs IOC 接口实例化

目前在开发程序时要实例化某一接口,而不知道接口是哪一个类实现,如果用Robotlegs来做是很好的。

我尝试用

injector.instantiate(IRobot);

但是程序始终报错,后来才发现接口是不能直接用instantiate方法来做。如果要用Injector来做,可以采用如下方法:

injector.getMapping(IRobot).getResponse() as IRobot;

平时一般向类中注入接口代码如下:

AS CODE:
  1. public class Robot implements IRobot {  
  2.         private var _leftLeg : ILeg;  
  3.         private var _rightLeg : ILeg;  
  4.   
  5.         public function get leftLeg() : ILeg {  
  6.             return _leftLeg;  
  7.         }  
  8.           
  9.         [Inject(name="leftLeg")]  
  10.         public function set leftLeg(leg : ILeg) : void {  
  11.             _leftLeg = leg;  
  12.         }  
  13.           
  14.         public function get rightLeg() : ILeg {  
  15.             return _rightLeg;  
  16.         }  
  17.           
  18.         [Inject(name="rightLeg")]  
  19.         public function set rightLeg(leg : ILeg) : void {  
  20.             _rightLeg = leg;  
  21.         }  
  22.     }  

上面代码中ILeg就是在Robot类实例化时注入,将会由Injector类自动调用injector.getMapping(ILeg).getResponse() as ILeg;

另外还有一个收获,如下代码,先映射Robot,IRobot为单例:

AS CODE:
  1. private function init(e:Event):void{  
  2.                 injector = new Injector();  
  3.                 injector.mapSingleton(Robot);  
  4.                 injector.mapSingletonOf(IRobot, Robot);  
  5.                   
  6.                 //injector.mapClass(IRobot, Robot);  
  7.                 injector.mapClass(ILeg, Leg,"leftLeg");  
  8.                 injector.mapClass(ILeg, Leg,"rightLeg");  
  9.                 injector.mapValue(String, "my robot no.1","robotName");  
  10.                   
  11.                 //injector.mapValue(IRobot, injector.instantiate(Robot));  
  12.             }  

再看如下代码:

AS CODE:
  1. var robot:IRobot = injector.getMapping(IRobot).getResponse() as IRobot;  
  2. var robotClone:Robot = injector.instantiate(Robot);  

这时的变量robot与robotClone是否为同一Robot对象实例的引用呢?

测试结果告诉我是相等的。

收藏-Flex Builder 3 on Eclipse 3.5

当我在机器上安装了flexbuilder3和STS(SpringSource Tool Suite )2.3后,将flexbuilder以插件的方式加入到STS,编译源文件和样式文件会出现编译出错,一开始是以为是上次编译产生的垃圾文件产生的问 题,采用项目文件清除后再编译,问题还是在。

ConfigurationProblem logging info

java.lang.IllegalArgumentException: "The attribute value type is com.adobe.flexbuilder.project.compiler.internal.ProblemManager and expected is one of java.lang.String, Boolean, Integer"
at org.eclipse.core.internal.resources.MarkerInfo.checkValidAttribute(MarkerInfo.java:84)
at org.eclipse.core.internal.resources.MarkerInfo.setAttributes(MarkerInfo.java:172)
at org.eclipse.core.internal.resources.Marker.setAttributes(Marker.java:304)
at com.adobe.flexbuilder.project.compiler.internal.ProblemManager.createMarker(Unknown Source)
at com.adobe.flexbuilder.project.compiler.internal.ProblemManager.createMarker(Unknown Source)
at com.adobe.flexbuilder.project.compiler.internal.ProblemManager.logProblem(Unknown Source)
at com.adobe.flexbuilder.project.compiler.internal.ProblemManager.logInternalProblem(Unknown Source)
at com.adobe.flexbuilder.project.compiler.internal.FlexProjectBuilder.buildItem(Unknown Source)
at com.adobe.flexbuilder.project.compiler.internal.FlexProjectBuilder.build(Unknown Source)
at com.adobe.flexbuilder.project.compiler.internal.FlexIncrementalBuilder.build(Unknown Source)
at org.eclipse.core.internal.events.BuildManager$2.run(BuildManager.java:627)
at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:170)
at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:201)
at org.eclipse.core.internal.events.BuildManager$1.run(BuildManager.java:253)
at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:256)
at org.eclipse.core.internal.events.BuildManager.basicBuildLoop(BuildManager.java:309)
at org.eclipse.core.internal.events.BuildManager.build(BuildManager.java:341)
at org.eclipse.core.internal.events.AutoBuildJob.doBuild(AutoBuildJob.java:140)
at org.eclipse.core.internal.events.AutoBuildJob.run(AutoBuildJob.java:238)
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55)

后来在网上找到是因为flexbuilder3采用eclipse3.3作为平台开发,而我是用eclipse3.5,需要做如下修改:

 

  1. 下载包 ProblemManager patch
  2. 解压下载的文件
  3. 到 Flex Builder安装目录
  4. 用winrar打开/eclipse/plugins/com.adobe.flexbuilder.project_3.0.204732/zornproject.jar文件
  5. 将刚才下载解压的文件目录 com拖到winrar中完成压缩
  6. 启动STS
  7. 成功编译项目

 英文网址:http://www.jamesward.com/2009/09/29/flex-builder-3-on-eclipse-3-5/?utm_campaign=BackType&utm_medium=bt.io-twitter&utm_source=&utm_content=backtype-tweetcount

 

Flexmojos Using ASDoc

flexmojos插件中,如何为Maven flexmojos构建的flex项目自动生成帮助文档?

可以有如下两种办法:

1.通过命令来生成,在构建的项目根目录中执行如下命令:
$ mvn org.sonatype.flexmojos:flexmojos-maven-plugin:asdoc

会在target/asdoc目录中生成HTML文件。

2.在项目pom.xml文件中进行配置,加入如下配置:
XML/HTML代码


<build>
<plugins>
<plugin>
<groupid>org.sonatype.flexmojos</groupid>
<artifactid>flexmojos-maven-plugin</artifactid>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>asdoc</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

当执行$mvn compile等命令时就会自动产生文档。

http://www.bonashen.com/attachments/month_0912/q20091226212711.JPG

收藏Flexmojos Adding libraries to compilation

When developing with flex, it is very common to need 3rd party libraries to build more elaborate applications. For example: if an application uses charts, the project will require datavisualization.swc.  If an application needs an SHA1 algorithm, it will require as3corelib, and so on.

Using adobe’s command line compilers (mxmlc and compc) you will use parameters from this list:
-compiler.external-library-path
-compiler.include-libraries
-compiler.library-path
-runtime-shared-library-path

But on flex-mojos those options are not available. If there is no option available, how does one add these libraries?

Simple, following maven way =D

Each required library must be added as a dependency. If you need datavisualization you will add a dependency like this:

<dependency>
  <groupId>com.adobe.flex.sdk</groupId>
  <artifactId>datavisualization</artifactId>
  <version>3.0.0.477</version>
  <type>swc</type>
</dependency>

One big question: How do you discover the dependency’s groupId and artifactId?
As with any module in maven, those who created the module must define their groupId and artifactId. Since flex is not very popular with the maven world, it most likely will not be available in any maven repository. If this is the case, you will define your own groupId/artifactId and install on your own repository.

Maven Guide to installing 3rd party JARs

Often times you will have 3rd party JARs that you need to put in your local repository for use in your builds. The JARs must be placed in the local repository in the correct place in order for it to be correctly picked up by Maven. To make this easier, and then error prone, we have provide a goal in the install plug-in which should make this relatively painless. To install a JAR in the local repository use the following command:
mvn install:install-file -Dfile= -DgroupId= -DartifactId= -Dversion= -Dpackaging=

You can follow the same logic for swc libraries.

Example for installing the Data Visualization swc’s into Maven.

Download data visualization swc’s from here, you need to install them into your maven repo as follows:

  • extract the zip file and cd to the datavisualization_sdk3.3/frameworks/libs and issue the mvn install command.
    mvn install:install-file -DgroupId=com.adobe.flex.framework -DartifactId=datavisualization -Dversion=3.3.0.4852 -Dpackaging=swc -Dfile=datavisualization.swc
    

  • do the same for /frameworks/locale/en_US and run the following commands:
    mvn install:install-file -DgroupId=com.adobe.flex.framework -DartifactId=datavisualization -Dversion=3.3.0.4852 -Dclassifier=en_US -Dpackaging=rb.swc -Dfile=datavisualization_rb.swc
    

You will need the matching dependencies in your pom:

<dependency>
   <groupId>com.adobe.flex.framework</groupId>
   <artifactId>datavisualization</artifactId>
   <version>3.3.0.4852</version>
   <type>swc</type>
 </dependency>
 <dependency>
   <groupId>com.adobe.flex.framework</groupId>
   <artifactId>datavisualization</artifactId>
   <classifier>en_US</classifier>
   <version>3.3.0.4852</version>
   <type>rb.swc</type>
 </dependency>

 

How do you define how the dependency should be used? If it should be external, how do you get it?
Defining dependency scope:

<dependency>
  <groupId>com.adobe.flex.sdk</groupId>
  <artifactId>datavisualization</artifactId>
  <version>3.0.0.477</version>
  <type>swc</type>
  <scope>external</scope>
</dependency>

Flex-mojos supports 6 scopes:

  • merged: this is the default value, when not defined will assume merged. That means SWC/SWF file will be bigger and self sufficient. Same as -compiler.library-path
  • internal: all dependency content will be included on target SWC/SWF. Biggest compiled file. Same as -compiler.include-libraries
  • external: no dependency content will be included on target SWC/SWF. Smaller compiled file. Makes no sense to use this scope on SWF compilation. Same as -compiler.external-library-path
  • rsl: no dependency content will be inclued on SWC/SWF. But, SWF will have a reference to load it a runtime. Do not use on SWC compilation. Same as -runtime-shared-library-path
  • caching: same as RSL, but uses adobe signed SWZ files.
  • test: libraries required to run tests. Same as -compiler.include-libraries, but at test template only!

flexmojos 创建flex项目常用构建命令

flexmojos has its own archetypes for flex application, so here they are:

Create library: $ mvn archetype:generate -DarchetypeRepository=http://repository.sonatype.org/content/groups/flexgroup -DarchetypeGroupId=org.sonatype.flexmojos -DarchetypeArtifactId=flexmojos-archetypes-library -DarchetypeVersion=[flexmojos targeted version]

Create application: $ mvn archetype:generate -DarchetypeRepository=http://repository.sonatype.com/content/groups/flexgroup -DarchetypeGroupId=org.sonatype.flexmojos -DarchetypeArtifactId=flexmojos-archetypes-application -DarchetypeVersion=[flexmojos targeted version]

Create modular application: $ mvn archetype:generate -DarchetypeRepository=http://repository.sonatype.org/content/groups/flexgroup -DarchetypeGroupId=org.sonatype.flexmojos -DarchetypeArtifactId=flexmojos-archetypes-modular-webapp -DarchetypeVersion=[flexmojos targeted version]

 

正常构建后的项目pom.xml文件结构如下:
XML/HTML代码
  1. <project xmlns="http://maven.apache.org/POM/4.0.0"  
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0  
  4. http://maven.apache.org/maven-v4_0_0.xsd">  
  5.   
  6.   <modelVersion>4.0.0</modelVersion>  
  7.   <groupId>ua.com.xoas</groupId>  
  8.   <artifactId>testapp</artifactId>  
  9.   <version>1.0</version>  
  10.   <packaging>swf</packaging>  
  11.   
  12.   <name>testapp Flex</name>  
  13.   
  14.   <build>  
  15.    <sourceDirectory>src/main/flex</sourceDirectory>  
  16.    <testSourceDirectory>src/test/flex</testSourceDirectory>  
  17.    <plugins>  
  18.     <plugin>  
  19.      <groupId>org.sonatype.flexmojos</groupId>  
  20.      <artifactId>flexmojos-maven-plugin</artifactId>  
  21.      <version>3.2.0</version>  
  22.      <extensions>true</extensions>  
  23.     </plugin>  
  24.    </plugins>  
  25.   </build>  
  26.   
  27.   <dependencies>  
  28.    <dependency>  
  29.     <groupId>com.adobe.flex.framework</groupId>  
  30.     <artifactId>flex-framework</artifactId>  
  31.     <version>3.2.0.3958</version>  
  32.     <type>pom</type>  
  33.    </dependency>  
  34.   
  35.    <dependency>  
  36.     <groupId>org.sonatype.flexmojos</groupId>  
  37.     <artifactId>flexmojos-unittest-support</artifactId>  
  38.     <version>3.2.0</version>  
  39.     <type>swc</type>  
  40.     <scope>test</scope>  
  41.    </dependency>  
  42.   </dependencies>  
  43.   
  44. </project>  

如果你要增加flexmojos的扩展参数需要如下配置:

 

XML/HTML代码
  1. <build>  
  2.  <sourceDirectory>src/main/flex</sourceDirectory>  
  3.  <testSourceDirectory>src/test/flex</testSourceDirectory>  
  4.  <plugins>  
  5.   <plugin>  
  6.     <groupId>org.sonatype.flexmojos</groupId>  
  7.     <artifactId>flexmojos-maven-plugin</artifactId>  
  8.     <version>3.2.0</version>  
  9.     <extensions>true</extensions>  
  10.     <configuration>  
  11.       <!–在这儿加入你的配置参数 –>  
  12.       <!–详细参数说明见
    http://sites.sonatype.org/flexmojos/flexmojos-maven-plugin/plugin-info.html 

    >  
  13.       <debug>true</debug>  
  14.       <allowSourcePathOverlap>true</allowSourcePathOverlap>  
  15.     </configuration>  
  16.   </plugin>  
  17.  </plugins>  
  18. </build>  

如果要将flexmojos生成的项目转换成flexbuilder项目请在创建的项目目录下执行如下命令:

$ mvn flexmojos:flexbuilder

目前在学习maven管理开发项目

在网上找到“构建全栈式flex、BlazeDS和Spring集成解决方案”并参考了http://www.riameeting.com/node/453的文章,正在构建nexus内网服务器,这样可以构建项目管理的基础设施。

目前遇到了通过mvn flexmojos:flexbuilder命令转换maven项目为flexbuilder项目后,通过flexbuilder导入发现无法可视编辑MXML文件,实在是头疼。

将最近学习MAVEN的资料上传上来,方便以后阅读。

MAVEN中文教程

构建全栈式Flex、BlazeDS和Spring集成解决方案

 

create and examine loaded Flex Modules

收藏自:lowpitch.com/blog/iflexmodulefactory-create-and-examine-loaded-flex-modules/

In an earlier post I described how you can load in flex modules using the ModuleManager class and the IModuleInfo interface, but I didn’t really mention IflexModuleFactory which is pretty fundamental to the whole process.

The factory is responsible for knowing how to create an instance of the module, and also provides access to certain properties of the module. It can get a little more complicated as the methods exposed by the factory become available at different stages of the load process, so you have to be careful not to try and retrieve information before it is available.

The factory is retrieved used by accessing the factory property of the IModuleInfo object, returned by ModuleManager.getModule (). For more information on this, see the previous post. The factory will be of type IFlexModuleFactory, which behind the scenes is implemented by mx.managers.SystemManager. You don’t really need to worry about this detail though, it’s enough to know about the two public methods exposed by the factory.

They are:

  • IFlexModuleFactory.create – This is really the most important method, and provides the mechanism for creating an instance of your loaded module. When enough of the module has been downloaded for this method to be called, the IModuleInfo object will dispatch a ModuleEvent.READY event, and the public property IModuleInfo.ready will return true. The method allows you to optionally pass in one or more parameters, using the “…rest” mechanism. These parameters are passed to your module’s constructor. Calling this method will create a new instance of your module, and the new instace of the method will be returned. The return type is specified as Object, so you will need to manually cast the returned object to the correct type in order to work with it.
  • IFlexModuleFactory.info – This module allows you to find out a bit of information about the module which is being loaded. When enough of the module has been downloaded for this method to be called, the IModuleInfo object will dispatch a ModuleEvent.SETUP event, and the public property IModuleInfo.setup will return true. The method returns an Object, containing a variety of name/value pairs providing bits of information about the module. Examples of the kind of information that can be contained in the object includes width, height, a list of mixins used by the module, a list of RSLs used by the modules, a list of fonts embedded within the module, and information about the modules Main class name. The method can be useful for finding out the module’s layout information before you create an instance of it and add it to the display list.
  • Just to mention a quick ‘gotcha’ that I encountered when first working with Modules… If you find that the call to IModuleData.factory.create for some reason returns null, or at some point in the process you get an Error #1009, most likely your module is using some classes which for some reason are not accessible once the SWFs have been compiled and optimised. A simple fix for this can be to reference the offending classes in some way in the main application to force them to be compiled. Common offenders are the ‘manager’ classes, for example DragManager, PopupManager and SystemManager.

 

loading Flex Modules dynamically

收藏自:lowpitch.com/blog

When you’re working with flex Modules, most of the time the ModuleLoader component will be enough to get you up and running. It will quickly and easily let you load your Modules and add them to the display list, it’ll even dispatch an event to let you know when the Module is ready. Lovely stuff. It’s very similar to the SWFLoader component with some added Module-related goodness thrown in.

However, there may be times when the ModuleLoader component may not be appropriate for your needs, and you’ll have to get your hands dirty and get involved with the lower-level ModuleManager class. Some examples of when ModuleLoader might not be suitable include:

  • Your Module is entirely non-visual, and therefore shouldn’t be added to the display list
  • Performance is really important to your application, and the extra overhead of the ModuleLoader container is something you need to remove to try and speed things up. (ModuleLoader essentially wraps your Module’s content in an extra container, incurring a slight performance hit).
  • You need greater control over how and when your modules are loaded and unloaded.

How does ModuleManager work?

ModuleManager maintains a map of modules, indexed by each module’s URL. Information about the module is stored as an object implementing IModuleInfo, and is retrieved by calling the ModuleManager.getModule method, passing in the module’s URL as the method’s only parameter. Once you’ve retrieved the IModuleInfo object, you can call its load method to starting loading in the module. While the module is loading, you can monitor the load progress using a variety of different events (of type ModuleEvent). Finally, when the module is loaded and available, you can create an instance of that module using someModuleInfo.factory.create ().

 

IModuleInfo in more detail

As mentioned above, ModuleManager maintains a mapping of URLs to modules. When you call ModuleManager’s static getModule method, it returns an object of type IModuleInfo. If you dig around within the ModuleManager class, you’ll see that this interface is implemented internally. It’s useful to know a little bit more about how you can interact with the ModuleInfo, what methods it exposes, and what events it dispatches.

There are four read-only public Boolean properties which offer information about the current state of the module. They can briefly be described:

  • IModuleInfo.error – This’ll be true if there was an error during loaded. You’re also notified of such an error by the ModuleEvent.ERROR event.
  • IModuleInfo.loaded – This will be true if you’ve previously called the IModuleInfo.load method. Note: It does not necessarily signify that the module has been loaded, just that the module load has begun.
  • IModuleInfo.setup – When enough of the module has loaded to be able to call the factory.info () method, this flag will be true. When this point in the load has been reached, a ModuleEvent.SETUP event will be dispatched.
  • IModuleInfo.ready – When enough of the module has been loaded for you to be able to instantiate an instance of the module using the factory.create () method, this flag will be true. At this stage, a ModuleEvent.READY event will be dispatched. This property will typically become true at some point after the IModuleInfo.setup becomes true.

There are some additional properties which can also be useful:

  • IModuleInfo.url fairly obviously returns the URL associated with the IModuleInfo object. This could be useful if you’re using one event handler to listen out for the events from multiple modules – the event.currentTarget.url property could be used to identify which module had sent out the event.
  • IModuleInfo.data is an object which you can use to associate data with a particular module. The data will be shared across all instances of a particular module, so this property comes in handy if you want to share state, or user information between multiple instances of the same dynamically loaded module.
  • IModuleInfo.factory is pretty important. Once the the module is ‘ready’ (after the ModuleEvent.READY event has been dispatched, or when IModuleInfo.ready returns true), you can create an instance of your module by calling the IflexModuleFactory.create method. Additionally, once the module has been ’setup’ (after ModuleEvent.SETUP has fired, or when IModuleInfo.setup returns true), you can query the IflexModuleFactory.info method to find out a little more about the module that has been loaded.

Next we should look at some of the methods exposed by an IModuleInfo:

  • IModuleInfo.load – This is the method you’ll call to start loading a module. You can optionally pass in information about the current ApplicationDomain and SecurityDomain if necessary. When the module is loading, events will be dispatched along the way as detailed below. Quite a nice feature here is that if the module has already been loaded, and you attempt to reload it, it’ll still dispatch the “setup” and “ready” events, meaning that you can use one set of event listener methods without needing to worry about whether the module is loading for the first time, or if it already exists in local memory. Snazzy…
  • IModuleInfo.release – This method will release the current reference to the module. Internally, when this method is called, a reference counter will be decremented. If no references to the module exist after this method has been called, it will automatically be unloaded.
  • IModuleInfo.unload – This triggers the unload of the module. It’s worth noting that if any references to the module exist, it will not be garbage collected. Also, calling this method won’t remove your module from the display list, you’ll have to do that too.

Lastly, let’s look at the events dispatched… All the events listed here are instances of ModuleEvent, and for some of the events you’ll be able to access information about the load progress through the event’s bytesLoaded and bytesTotal properties.

  • ModuleEvent.ERROR – Rather obviously, this is dispatched when an error occurs during loading. This will also be indicated by the IModuleInfo.error property returning true.
  • ModuleEvent.PROGRESS – This event is dispatched periodically during the load, giving you the chance to display a visual indication of load progress if you want. This is one of the events where the bytesLoaded and bytesTotal properties will be set.
  • ModuleEvent.SETUP – This event is dispatched when enough of the module has downloaded for you to call the IModuleInfo.factory.info method. At this point, IModuleInfo.setup will also return true. This event does not allow you to access load progress through the bytesLoaded and bytesTotal properties.
  • ModuleEvent.READY – This event is dispatched when enough of the module has downloaded for you to create an instance of the module through the IModuleInfo.factory.create method. From then on, IModuleInfo.ready will also return true. Apparently, this event is one where the bytesLoaded and bytesTotal properties should be set, although I’ve noticed that this isn’t always the case. I reckon it’s safer just to use the ModuleEvent.PROGRESS information for displaying progress information.
  • ModuleEvent.UNLOAD – This event is dispatched when the module has been unloaded. No progress information is available through the event object with this event.

A simple example

First, a basic module, SimpleModule, which does nothing more than display a label within a panel.

SimpleModule.mxml
XML/HTML代码
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">  
  3.     <mx:TitleWindow x="10" y="10" width="120" height="100" layout="absolute">  
  4.         <mx:Text text="SimpleModule!" />  
  5.     </mx:TitleWindow>  
  6. </mx:Module>  

The the main application, which will load in this module and attach instances to a Tile control.

Shell.mxml

XML/HTML代码
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"  
  3.     applicationComplete="initApp()">  
  4.     <mx:Script>  
  5.         <![CDATA[ 
  6.             import mx.modules.Module; 
  7.             import mx.events.ModuleEvent; 
  8.             import mx.modules.ModuleManager; 
  9.             import mx.modules.IModuleInfo; 
  10.   
  11.           protected var _moduleInfo:IModuleInfo; 
  12.              
  13.             private function initApp():void { 
  14.                addToLog ("Application initialised"); 
  15.                // create the module - note, we're not loading it yet 
  16.                 _moduleInfo = ModuleManager.getModule("SimpleModule.swf"); 
  17.                // add some listeners 
  18.                _moduleInfo.addEventListener(ModuleEvent.READY, onModuleReady); 
  19.                _moduleInfo.addEventListener(ModuleEvent.SETUP, onModuleSetup); 
  20.                _moduleInfo.addEventListener(ModuleEvent.UNLOAD, onModuleUnload); 
  21.                _moduleInfo.addEventListener(ModuleEvent.PROGRESS, onModuleProgress); 
  22.             } 
  23.             
  24.             protected function getModuleInfo () : IModuleInfo { 
  25.                // return the module info 
  26.                return _moduleInfo; 
  27.             } 
  28.             
  29.             /** 
  30.              * Adds output to the log 
  31.              **/ 
  32.             protected function addToLog (msg:String) : void { 
  33.                 log.text += msg + "\n"; 
  34.                 // scroll to the bottom on the next frame 
  35.                 callLater(scrollToBottom); 
  36.                 
  37.             } 
  38.             
  39.             protected function scrollToBottom () : void { 
  40.                 // scroll to the bottom 
  41.                 log.verticalScrollPosition = log.maxVerticalScrollPosition; 
  42.             } 
  43.             
  44.             /** 
  45.             * Called when the "load" button is pressed 
  46.             * 
  47.             * Starts loading the module - when the module has been 
  48.             * loaded, an instance of the module will be created 
  49.             * and added to the tile control 
  50.             * 
  51.             **/ 
  52.             private function loadModule () : void { 
  53.                 addToLog ("Loading module"); 
  54.                 // load the module 
  55.                 getModuleInfo().load(); 
  56.             } 
  57.     
  58.             /** 
  59.              * Called when the "unload" button is pressed 
  60.              * 
  61.              * Removes all the instances of the module from the 
  62.              * tile control, then unloads the module 
  63.              * 
  64.              */ 
  65.             private function unloadModule () : void { 
  66.                 addToLog ("Unloading module"); 
  67.                 // clear out all the the instances 
  68.                 // of the module from the tile 
  69.                 tile.removeAllChildren(); 
  70.                 // unload the module 
  71.                 getModuleInfo().release(); 
  72.                 //getModuleInfo().un 
  73.             } 
  74.              
  75.             /** 
  76.             * Handler for the ModuleEvent.PROGRESS event 
  77.             **/ 
  78.             protected function onModuleProgress (e:ModuleEvent) : void { 
  79.                 addToLog ("ModuleEvent.PROGRESS received: " + e.bytesLoaded + " of " + e.bytesTotal + " loaded."); 
  80.             } 
  81.              
  82.             /** 
  83.             * Handler for the ModuleEvent.SETUP event 
  84.             **/ 
  85.             private function onModuleSetup (e:ModuleEvent) : void { 
  86.                 addToLog ("ModuleEvent.SETUP received"); 
  87.                 // cast the currentTarget 
  88.                 var moduleInfo:IModuleInfo = e.currentTarget as IModuleInfo; 
  89.                 addToLog ("Calling IModuleInfo.factory.info ()"); 
  90.                 // grab the info and display information about it 
  91.                 var info:Object = moduleInfo.factory.info(); 
  92.                 for (var each:String in info) { 
  93.                     addToLog ("     " + each + " = " + info[each]); 
  94.                 } 
  95.                 
  96.             } 
  97.              
  98.             /** 
  99.             * Handler for the ModuleEvent.READY event 
  100.             **/ 
  101.             private function onModuleReady (e:ModuleEvent):void { 
  102.                 addToLog ("ModuleEvent.READY received"); 
  103.                 // cast the currentTarget 
  104.                 var moduleInfo:IModuleInfo = e.currentTarget as IModuleInfo; 
  105.                 // Add an instance of the module’s class to the 
  106.                 // display list. 
  107.                 addToLog ("Calling IModuleInfo.factory.create ()"); 
  108.                 tile.addChild( moduleInfo.factory.create () as SomeModule); 
  109.                 addToLog ("SomeModule instance created and added to Display List"); 
  110.             } 
  111.              
  112.             /** 
  113.             * Handler for the ModuleEvent.UNLOAD event 
  114.             **/ 
  115.             public function onModuleUnload (e:ModuleEvent) : void { 
  116.                 addToLog ("ModuleEvent.UNLOAD received"); 
  117.             } 
  118.              
  119.         ]]>  
  120.     </mx:Script>  
  121.      
  122.     <mx:HBox width="100%" height="100%">  
  123.         <mx:Tile id="tile" width="100%" height="100%" />  
  124.            <mx:TextArea id="log" width="100%" height="100%"/>  
  125.     </mx:HBox>  
  126.      
  127.      
  128.     <mx:ApplicationControlBar>  
  129.         <mx:Button label="Load" click="loadModule ()" />  
  130.         <mx:Button label="Unload" click="unloadModule ()"/>  
  131.     </mx:ApplicationControlBar>  
  132.      
  133. </mx:Application>  

What’s going on here? Well… when the app is created, initApp is called and the IModuleInfo object is created, and various event listeners are setup. When the user clicks the load button, the module is loaded. It doesn’t matter whether this is the first time the module is loaded, or if the module is already loaded and ready, the ModuleEvent.READY event will be dispatched to let us know that the module is ready, and that we can now create an instance of it. Within the handler for the ModuleEvent.READY event, we call IModuleInfo.factory.create () and add the instance of the module to our Tile control.

When the user clicks the unload button, all instances of the Module that we’ve added to the Tile control are removed, and we call IModuleInfo.unload to unload the module. A log message from within the handler for ModuleEvent.UNLOAD demonstrates that this is happening.

Additionally, within the event handler for ModuleEvent.SETUP we show an example of the IModuleInfo.factory.info method – this’ll give you an example of the kind of information you can retrieve about the module, and from looking at the log messages you’ll be able to see the order in which these events are dispatched.

Lastly, here’s the finished example:

 (Update: I’ve explained the IFlexModuleFactory class in a little more detail here.)

FDT 3.5 beta 破解

目前FDT已经发布了3.5beta版,下面是新功能的介绍EN版的。

What’s new in FDT 3.5

With version 3.5 of FDT we are introducing great new features for both MXML and ActionScript editor. While we are focused on bringing you the best MXML editor for flex development at this time, we are continually improving FDT.

There will be two beta testing phases before final release.

The public beta of FDT 3.5 Beta 1 is released since august 27th. Public beta release for FDT 3.5 Beta 2 is scheduled for the next weeks.

Update: Mac Snow Leopard users should update to the latest beta Version 3.5 Build 1033, FDT provides full support in this version.

Features and Improvements of FDT 3.5 beta 1

Autocompletion for MXML Tags

Autocompletion for event types

Watch that feature in action

Improved Quick Outline

Autocompletion for constant convention

Watch that feature in action

Semantic Highlighting and Autocompletion for inline XML

Watch that feature in action

New Quick Fixes

Watch that feature in action

Brand new Debugger

Variable folding and XML Preview are the new features in the debugger

Features and Improvements of FDT 3.5 beta 2

  • Autocompletion for ActionScript in MXML files
  • Search and Find References in MXML files

安装有两种方式,一种是all in one,一种是eclipse plugin。

FDT 3.5 beta for Windows

How to install FDT 3.5 beta in Eclipse?

  1. Install Eclipse (www.eclipse.org)
  2. Open Eclipse
  3. Select "Help->Software Updates->Find and install…"
  4. Select "Search for new features to install" and press next
  5. Press "New Remote Site…"
  6. Insert FDT into Name and "http://fdt.powerflasher.com/update_beta/" into URL
  7. Press finish

点击下载此文件

解压后放在eclipse plugin\安装目录com.powerflasher.fdt.core_3.5.0.1040文件夹中。

破解后的效果如下:

自定义 Tree Data descriptor

在做开源供应链管理系统的时候,会用到很多树形的描述,需要用到mx.controls.Tree来展现,而数据库表定义时,表内有相关联如下:

SQL代码

create table "materialStock"."dbo"."MaterialType"(
"autoId" _autoId not null,
"parentId" _autoId null,
"unit" _unit(20) null,
"name" _name(20) null,
constraint "XPKMaterialType" primary key ("autoId")
)
go

alter table "materialStock"."dbo"."MaterialType"
add constraint "R_6"
foreign key ("parentId")
references "materialStock"."dbo"."MaterialType"("autoId")
go
create unique index "XPKMaterialType" on "materialStock"."dbo"."MaterialType"("autoId")
go

通过JPA就自动生成如下JAVA代码:
Java代码

package com.csgjs.mm.model.domain;

import …

/**
* AbstractMaterialType entity provides the base persistence definition of the
* MaterialType entity.
*
* @author MyEclipse Persistence Tools
*/
@MappedSuperclass
public abstract class AbstractMaterialType implements java.io.Serializable {

// Fields

private Integer autoId;
private MaterialType materialType;
private String unit;
private String name;
private Set<MaterialType> materialTypes = new HashSet<MaterialType>(0);

// Constructors

…

public void setMaterialType(MaterialType materialType) {
this.materialType = materialType;
}

…

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "materialType")
public Set<MaterialCatalog> getMaterialCatalogs() {
return this.materialCatalogs;
}

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "materialType")
public Set<MaterialType> getMaterialTypes() {
return this.materialTypes;
}

public void setMaterialTypes(Set<MaterialType> materialTypes) {
this.materialTypes = materialTypes;
}

}

再由granite data service自动生成如下AS3类:

ActionScript代码

public class AbstractMaterialTypeBase implements IExternalizable {

private var __initialized:Boolean = true;
private var __detachedState:String = null;

private var _autoId:Number;
private var _materialType:MaterialType;
private var _materialTypes:ListCollectionView;
private var _name:String;
private var _unit:String;

meta function isInitialized(name:String = null):Boolean {
if (!name)
return __initialized;

var property:* = this[name];
return (
(!(property is AbstractMaterialType) || (property as AbstractMaterialType).meta::isInitialized()) &&
(!(property is IPersistentCollection) || (property as IPersistentCollection).isInitialized())
);
}

public function set autoId(value:Number):void {
_autoId = value;
}
public function get autoId():Number {
return _autoId;
}

public function set materialType(value:MaterialType):void {
_materialType = value;
}
public function get materialType():MaterialType {
return _materialType;
}

public function set materialTypes(value:ListCollectionView):void {
_materialTypes = value;
}
public function get materialTypes():ListCollectionView {
return _materialTypes;
}

public function set name(value:String):void {
_name = value;
}
public function get name():String {
return _name;
}

public function set unit(value:String):void {
_unit = value;
}
public function get unit():String {
return _unit;
}

public function readExternal(input:IDataInput):void {
__initialized = input.readObject() as Boolean;
__detachedState = input.readObject() as String;
if (meta::isInitialized()) {
_autoId = function(o:*):Number { return (o is Number ? o as Number : Number.NaN) } (input.readObject());
_materialType = input.readObject() as MaterialType;
_materialTypes = input.readObject() as ListCollectionView;
_name = input.readObject() as String;
_unit = input.readObject() as String;
}
else {
_autoId = function(o:*):Number { return (o is Number ? o as Number : Number.NaN) } (input.readObject());
}
}

public function writeExternal(output:IDataOutput):void {
output.writeObject(__initialized);
output.writeObject(__detachedState);
if (meta::isInitialized()) {
output.writeObject(_autoId);
output.writeObject(_materialType);
output.writeObject(_materialTypes);
output.writeObject(_name);
output.writeObject(_unit);
}
else {
output.writeObject(_autoId);
}
}
}
}

当调用远程服务返回的数据是一个ArrayCollection,包含所有的MaterialType的数据集合。

但是Tree的默认的dataDescriptor是查找children属性,可是MaterialType中没有,只有 materialTypes包含子node.

所以在mx.controls.treeClasses.DefaultDataDescriptor继承新类重载getChildren和 isBranch两个方法。

代码如下:

ActionScript代码

package org.bona.controls
{
import flash.utils.Dictionary;

import mx.collections.ArrayCollection;
import mx.collections.ICollectionView;
import mx.collections.XMLListCollection;
import mx.controls.treeClasses.DefaultDataDescriptor;
import mx.controls.treeClasses.ITreeDataDescriptor;

public class TreeDataDescriptor extends DefaultDataDescriptor implements ITreeDataDescriptor
{
/**
*  Constructor.
*/
public function TreeDataDescriptor(children:String=null)
{
super();
childrenField=children;
//trace(‘TreeDataDescriptor Constructor, ’ + childrenField);
}

/**
*  @private
*/
private var ChildCollectionCache:Dictionary=new Dictionary(true);

[Bindable]
public var childrenField:String=null;

override public function getChildren(node:Object, model:Object=null):ICollectionView
{
//          trace(node);
var childrenCollection:ICollectionView=super.getChildren(node, model);
var children:*;
if (childrenCollection != null)
{
//              trace(’super.getChildren =’, childrenCollection);
return childrenCollection;
}
children=node[childrenField];
//          trace(children);

//then wrap children in ICollectionView if necessary
if (children is ICollectionView)
{
childrenCollection=ICollectionView(children);
}
else if (children is Array)
{
var oldArrayCollection:ArrayCollection=ChildCollectionCache[node];
if (!oldArrayCollection)
{
childrenCollection=new ArrayCollection(children);
ChildCollectionCache[node]=childrenCollection;
}
else
{
childrenCollection=oldArrayCollection;
//ArrayCollection(childrenCollection).mx_internal::dispatchResetEvent=false;
ArrayCollection(childrenCollection).source=children;
}

}
else if (children is XMLList)
{
var oldXMLCollection:XMLListCollection=ChildCollectionCache[node];
if (!oldXMLCollection)
{
// double check since XML as dictionary keys is inconsistent
for (var p:*in ChildCollectionCache)
{
if (p === node)
{
oldXMLCollection=ChildCollectionCache[p];
break;
}
}
}

if (!oldXMLCollection)
{
childrenCollection=new XMLListCollection(children);
ChildCollectionCache[node]=childrenCollection;
}
else
{
childrenCollection=oldXMLCollection;

//We don’t want to send a RESET type of collectionChange event in this case.
//XMLListCollection(childrenCollection).mx_internal::dispatchResetEvent=false;
XMLListCollection(childrenCollection).source=children;
}
}
else
{
var childArray:Array=new Array(children);
if (childArray != null)
{
childrenCollection=new ArrayCollection(childArray);
}
}
return childrenCollection;
}

/**
*  Tests a node for termination.
*  Branches are non-terminating but are not required to have any leaf nodes.
*  If the node is XML, returns <code>true</code> if the node has children
*  or a <code>true isBranch</code> attribute.
*  If the node is an object, returns <code>true</code> if the node has a
*  (possibly empty) <code>children</code> field.
*
*  @param node The node object currently being evaluated.
*  @param model The collection that contains the node; ignored by this class.
*
*  @return <code>true</code> if this node is non-terminating.
*/
override public function isBranch(node:Object, model:Object=null):Boolean
{
var branch:Boolean = super.isBranch(node,model);
if (branch)return branch;
if (node == null)
return false;
if (node is Object)
{
try
{
if (node[childrenField] != undefined)
{
branch=true;
}
}
catch (e:Error)
{
}
}
return branch;
}

}

}

在应用程序模块中如下应用:

MXML代码


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:tdd="org.bona.controls.*" creationComplete="{initData();}" viewSourceURL="srcview/index.html">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var datas:ArrayCollection = new ArrayCollection();
[Bindable]
private var selectedItem:MaterialType;

private function initData():void{
var mt:MaterialType = generateMaterialType(‘level top -1′);
generateMaterialType(‘level 2-1′,mt);
generateMaterialType(‘level 2-2′,mt);
generateMaterialType(‘level 2-3′,mt);
datas.addItem(mt);
mt = generateMaterialType(‘level top -2′);
datas.addItem(mt);
}

private function generateMaterialType(name:String,parent:MaterialType=null):MaterialType{
var mt :MaterialType = new MaterialType();
mt.name = name;
if(parent !=null){
if(parent.materialTypes==null){
parent.materialTypes = new ArrayCollection();
}
parent.materialTypes.addItem(mt);
}
return mt;
}

private function selected(e:Event):void{
selectedItem = Tree(e.target).selectedItem as MaterialType;
}

]]>
</mx:Script>
<mx:HDividedBox width="100%" height="100%">
<mx:Tree dataProvider="{datas}" labelField="name" height="100%" width="100%" change="selected(event);">
<mx:dataDescriptor>
<tdd:TreeDataDescriptor childrenField="materialTypes"/>
</mx:dataDescriptor>
</mx:Tree>
<mx:VBox width="100%" height="100%">
<mx:Label text="selected material type:"/>
<mx:TextArea width="100%" height="100%" text="{selectedItem.name}"/>
</mx:VBox>

</mx:HDividedBox>

</mx:Application>

这样就可以自定义childrenField来描述自己的结构。

查看在线Demo