Creating Flex UIs in Java – A Short Tutorial
Dieser Artikel wurde von Johannes Koch geschrieben. Johannes ist Student der Informatik an der TU Dresden. Er arbeitet derzeit im Rahmen seiner Diplomarbeit als Softwareentwickler bei der Firma ubigrate mit den Schwerpunkten XML, Java und Codegenerierung.
You probably know two or three different ways to create Adobe Flash UIs. The most straightforward and expensive one is using Adobe’s Flex Builder. This IDE offers a quick, flexible and reliable way to program a UI in MXML – Adobe’s XML-based UI description language – and ActionScript – the corresponding scripting language to give life to the MXML UI components. For students, Lehman stock owners and other less pecunious people, there is the Flex 3 SDK and a bunch of tutorials on the web on how to teach Eclipse MXML and ActionScript. But did you ever think about using Java to write a Flash UI? You could integrate a fancy Flash UI with your Java application more smoothly, or you could write a Flex code generator in Java (maybe even provide other developers with a DSL that your Java-based generator can translate into Flex applications?). Using only free software, it doesn’t take more than a couple of minutes to create such a Java library for Flex Applications – this tutorial shows you how to do it.
Notice: If you’re not interested in details and just want to get your Java library quickly, you can skip step one and most of step 2 and simply download the pre-built customized mxml.xsd file.
Step One: Obtaining an XML Schema File for MXML
MXML is Adobe’s XML-based description language for UIs written with the Flex 3 library. Unfortunately, Adobe doesn’t provide an XML Schema for MXML Version 3, although theoretically, since MXML is XML-based, there is a way to describe the MXML syntax in XML Schema. There are, however, other sources for this information coming with the Flex 3 SDK, which can be used to extract the MXML syntax information and generate a schema document for MXML from it. Luckily, Ali Mansuroglu already did this work for us and published the schema generator xsd4mxml. If you download and import the project for your own use and/or customization, be sure to note and fix the bug described in issue 1 of the project. For the purpose of this tutorial, it’s sufficient to download the ready-made mxml.xsd file from the issue description on the xsd4mxml project home page.
Step Two: Compiling the MXML Schema File with JAXB
JAXB, the “Java Architecture for XML Binding”, is an API for the mapping of Java classes to XML documents and vice versa. So, here is the decisive point: Since Flex applications are MXML documents, and MXML documents are XML documents, JAXB can create Flex applications from Java objects. This creation is called marshalling the Java objects. But where can we get Java classes suitable to create objects that can be marshalled into valid MXML files? The answer is called XJC, a Schema-to-Java compiler that comes with Sun’s JAXB Reference Implementation (JAXB RI). XJC can compile the MXML schema document from step one to such a set of Java classes. Instances of these classes can be marshalled into valid MXML documents by JAXB, which means nothing else than enabling you to write Flex applications in Java. The good part is that you can use this “homegrown” library to create Flash UIs programmatically very much alike the way you may be used to from your favorite Java UI framework like Swing or SWT. The unpleasant part is that the ActionScript code part of the created Flex application will still have to be written manually. Your Java library will provide an mx:script
object that you can fill with ActionScript code in form of a String in Java, but the editing of the script itself is not supported by any Java resources.
You could, however, use the library to generate a UI in MXML programmatically and add ActionScript code afterwards in your favorite UI, e.g. Adobe Flex Builder.
Download the latest version (2.1.9 when this was written) of the JAXB RI from the JAXB homepage, and unzip it to a directory of your choice, let’s call it JAXB_HOME. XJC can be found in JAXB_HOME/jaxb-ri/bin. Either put the XJC executable on your system’s PATH, or simply copy the mxml.xsd file to JAXB_HOME/jaxb-ri/bin. This is of course not really good practice, but for this one time, let’s just do it.
When you now go to the JAXB_HOME/jaxb-ri/bin directory and call xjc mxml.xsd
, it’s most likely that you’ll encounter a java.lang.OutOfMemoryError
, because the schema document created by xsd4mxml is quite large (over 1 MB) and therefore memory-intensive to compile. To provide XJC with the needed memory, edit the XJC launching script (xjc.bat
for Windows and xjc.sh
for Linux) with your favorite text editor and add the VM option -Xmx1024m
to the XJC launch. This will assign 1 GB of memory to XJC and should be sufficient to compile the mxml.xsd file. For Windows, it looks like this (sorry, don’t know about Linux):
:LAUNCHXJC %JAVA% -Xmx1024m %XJC_OPTS% -jar "%JAXB_HOME%\lib\jaxb-xjc.jar" %*
If you still encounter an OutOfMemoryError, providing XJC with even more memory (e.g. using -Xmx2048m
instead of -Xmx1024
) or closing large running programs like Eclipse may help.
After resolving the OutOfMemoryError, you are ready for the next error. When XJC compiles the mxml.xsd file generated by xsd4mxml with a sufficient amount of memory, it’ll tell you that “two declarations cause a collision in the ObjectFactory class”, stating the line numbers 2857 and 5806. The reason here is that XJC automatically creates the Java identifiers for the generated Java classes and their members from the MXML schema from step one, and the elements “ct_DataGrid.columnWidth” and “ct_DataGridColumn.width” cause a duplicate Java identifier in one of the generated classes (namely ObjectFactory, more about this class later). For cases like this, when the automatic creation of Java identifiers by JAXB’s standard binding rules doesn’t suffice, users can define custom bindings, i.e. custom names for generated Java language elements. The quick-and-easy inline definition of a custom binding that resolves our naming conflict looks like this:
The <appinfo>
tag tells XJC to name the generated class “CtDataGridColumn123″ instead of “CtDataGridColumn”, which would have been the automatically chosen name.
A modified version of the mxml.xsd file containing the above customization can be downloaded here (rename to mxml.xsd after download).
Now you are finally ready to create your Java library for Flex applications by calling xjc mxml.xsd
. XJC will generate one Java class per Flex UI component into the package com.adobe._2006.mxml
, which should look like this:
For easier handling, I recommend to jar the generated Java files. Provided the jar command is on your system’s PATH, the command would look like this:
jar cvf mxml.jar com/adobe/_2006/mxml/*
Step Three: Setting up the Java Project
Assuming you are already somewhat familiar with your development environment, I’ll try to keep this short.
Create a new Java project and a folder named “lib” inside it. Copy the mxml.jar file from step 2 to the lib folder as well as the JAXB libraries (the .jar files from JAXB_HOME/jaxb-ri/lib, namely activation.jar, jaxb1-impl.jar, jaxb-api.jar, jaxb-impl.jar, jaxb-xjc.jar, and jsr173_1.0_api.jar). Put these libraries on the project’s classpath (in Eclipse: Project Properties –> Libraries –> Add JARs…). Create a new package, let’s say com.ubigrate.tutorial.flex, and add two new classes to this package, one named MXMLGenerator, and one named GeneratorTestRun. Add the two methods public void createMXMLApplication()
and public void marshalApplication()
to class MXMLGenerator and the following main method to class GeneratorTestRun:
public static void main(String[] args) { MXMLGenerator g = new MXMLGenerator(); g.createMXMLApplication(); g.marshalApplication(); }
In Eclipse, the project should now look like this:
Step Four: Creating MXML Widgets in Java
Now that the frame is set, we’ll finally add the MXML genration code to the MXMLGenerator. Let’s start with the createMXMLApplication()
method.
Create a new ObjectFactory:
private static ObjectFactory mxmlFactory = new ObjectFactory();
This ObjectFactory is where you can obtain instances of all the Flex UI components from.
Use it to create the MXML application:
CtApplication application = mxmlFactory.createCtApplication();
Theoretically, we could already marshal this CtApplication object to become an empty Flex application, but practically we can’t, because it’s missing some information necessary to become a fully-grown XML element, namely namespace information. To add this namespace information, we’ll create a JAXBElement
wrapper object. To be able to use the application wrapper object in other methods as well, declare it on class level (outside any method):
private JAXBElement<CtApplication> applicationWrapper;
Now go back to the createMXMLApplication()
method and obtain the application wrapper object from the ObjectFactory:
applicationWrapper = mxmlFactory.createApplication(application);
The first UI elements should follow a vertical box layout, so we’ll obtain a VBox object from the ObjectFactory. Again, to be able to marshal the VBox later on, a JAXB wrapper object is necessary:
CtVBox vBox = mxmlFactory.createCtVBox(); JAXBElement<CtVBox> vBoxWrapper = mxmlFactory.createVBox(vBox);
Add the VBox wrapper to the application’s content, i.e. the elements and text that will appear between the opening and closing tags of the application in the generated MXML file:
application.getContent().add(vBoxWrapper);
It is a good idea to follow this pattern for all the MXML elements you create: First obtain the element from the ObjectFactory, then wrap it up in a JAXBElement, and finally add the wrapper object to the parent object’s content. Let’s apply this to the creation of our next UI element, a Panel:
CtPanel panel1 = mxmlFactory.createCtPanel(); JAXBElement<CtPanel> panel1Wrapper = mxmlFactory.createPanel(panel1); vBox.getContent().add(panel1Wrapper);
This time we’re going to set some extra properties of the created element, namely an ID and a title:
panel1.setId("p1"); panel1.setTitle("Panel 1");
Repeat these steps for two more panels. You can simply copy&paste the above five lines and exchange the “1″ for “2″ and “3″, respectively.
To add some variety, let’s add two buttons, this time in a horizontal box layout. First the layout object:
CtHBox hBox = mxmlFactory.createCtHBox(); JAXBElement<CtHBox> hBoxWrapper = mxmlFactory.createHBox(hBox); application.getContent().add(hBoxWrapper);
Now the first button:
CtButton button1 = mxmlFactory.createCtButton(); JAXBElement<CtButton> button1Wrapper = mxmlFactory.createButton(button1); hBox.getContent().add(button1Wrapper);
We’ll give the button a line of ActionScript code to execute when it is clicked and a meaningful label:
button1.setClick("{p2.visible=!p2.visible;}"); button1.setLabel("Toggle Panel 2 Visible");
Create a second button the 3-step way you already know and give it this slightly different functionality:
button2.setClick("{p2.includeInLayout=!p2.includeInLayout;}"); button2.setLabel("Toggle Panel 2 in Layout");
Now we have a nice example UI and are done filling up the createMXMLApplication()
method.
Step Five: Marshalling the Application
The final step is to marshal the application object (specifically: the application wrapper object) assembled in step 2. Fill up the marshalAppliation()
method as follows:
Open a try
block and create a new JAXBContext from the created package name and the currently used ClassLoader:
try { JAXBContext mxmlContext = JAXBContext.newInstance ("com.adobe._2006.mxml", this.getClass().getClassLoader());
(Using the wrong ClassLoader here can get you a really nice, fully-grown WOMBAT.)
Obtain a marshaller from the context and tell it to deliver nicely formatted output:
Marshaller marshaller = mxmlContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
And now, finally, have the marshaller write the mxml file:
marshaller.marshal(applicationWrapper, new FileWriter("app.mxml"));
Notice: To avoid a naming conflict with the MXML element “Application”, do not name your output file “application.mxml”.
To finish up, let the user know that the mxml file is ready and write a catch
branch for the zoo of exceptions that can occur in the try branch:
System.out.println("Marshalling done."); } catch(Exception e) { throw new RuntimeException(e); }
You should now be able to compile the classes and run GeneratorTestRun.main()
without errors.
The full source code of class MXMLGenerator looks like this (for better viewing, you can download the file and rename the file extension to .java).
Step Six: Compiling and Testing the Flex Application
If everything went right, a file named app.mxml was generated in the root of your project (or wherever else you executed GeneratorTestRun.main()
). If you use Eclipse and don’t see the file, try refreshing the project.
The app.mxml file should look almost the same as the third example of Adobe’s guide to “Positioning and laying out controls”. This MXML file can now be passed to the Flex compiler, which will render a nice Flash UI from it. Either import the MXML file to a project in your Flex builder or call mxmlc
from the bin directory of your Flex SDK. For a quick and dirty test run, copy the generated app.mxml file to this bin directory and call mxmlc
, which in Windows looks like this:
The output of this call is a file named “app.swf”, which can be opened with the standalone Flash Player, or your favorite web browser with a current Flash Player plugin installed. What you get to see should look a lot like this:
Done!
This concludes this tutorial about creating a Java library for writing Flex applications. I hope you found it interesting and am looking forward to your comments.
An Eclipse project containing the prospective outcome of this tutorial (excluding the JAXB libraries) can be downloaded here.
Useful links: JAXB API, MXML Controls API
J Cruz sagt
am 19. März 2009 @ 2:07
Excellent tutorial.
It’s nice to see that all steps are covered, (including the errors and how to handle them).
It’s also very nice to be able to generate MXML and then the SWF file all within a few java classes.
I can’t wait to play with the JAXB-generated classes.
Jay sagt
am 23. Dezember 2009 @ 16:30
Thanks for the great Tutorial, it’s great when people share this sort of concise knowledge. I will pay it forward.
Md Sirajuddin sagt
am 10. Oktober 2011 @ 6:06
Fruitful Information…