Convert FXML to Java as part of the build


FXML is a declarative way to define you JavaFX UIs, you can edit it by hand e.g. useing e(fx)clipse, use a DSL like FXGraph or use a WYSIWYG-Tool like SceneBuilder.

At runtime a class named FXMLLoader takes the FXML and creates a SceneGraph out of it using reflection. Let’s look at a small sample.

FXML:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.Button?>

<BorderPane xmlns:fx="http://javafx.com/fxml">
  <center>
    <Button text="Hello World"></Button>
  </center>
</BorderPane>

Loading in Java:

BorderPane p = FXMLLoader.load(getClass().getClassLoader().getResource("Sample.fxml"));

Simple & easy BUT it is slow. On desktop system the slowness might not bother you but on constrainted devices like Raspberry Pi or smartphone reflection it will.

So the idea I had already since some time [Bug] is to move translation from runtime to build time so that one can simply add an instruction to the build script and FXML-Files are translated to Java-Files and compiled by the Java compiler.

While not yet complete I can now share the first version which does at least supports the most basic stuff. To build my JavaFX app which uses FXML I can now use an ant-file like this:

<project default="test">
  <path id="fxcompile">
    <filelist>
      <file name="build/classes"/>
      <file name="build-libs/org.eclipse.fx.fxml.compiler_0.9.0-SNAPSHOT.jar"/>
    </filelist>
  </path>
	
  <path id="buildpath">
    <filelist>
      <file name="build/classes"/>
      <file name="libs/org.eclipse.fx.core_0.9.0.201308281625.jar"/>
    </filelist>
  </path>
	
 <target name="test">
   <delete dir="build" />
   <mkdir dir="build/classes"/>
		
   <taskdef name="fxml-compiler" classpathref="fxcompile" classname="org.eclipse.fx.ide.fxml.compiler.ant.FXMLCompilerTask" />
		
   <javac srcdir="src" destdir="build/classes" classpathref="buildpath"></javac>
		
   <fxml-compiler sourcedir="src" destdir="build/gen-src"/>
   <javac srcdir="build/gen-src" destdir="build/classes" classpathref="buildpath"></javac>
  </target>
</project>

The important line is <fxml-compiler… which instructs the build to translate all FXML-Files found below src into .java-Files and place them into build/gen-src.

Generated Java-File:

import javafx.fxml.FXMLLoader;
import javafx.scene.layout.BorderPane;
import javafx.scene.control.Button;

import java.util.Map;
import java.net.URL;
import java.util.ResourceBundle;

import org.eclipse.fx.core.fxml.FXMLDocument;

import java.util.HashMap;

@SuppressWarnings("all")
public class Sample extends FXMLDocument<BorderPane> {
  private Map<String,Object> namespaceMap = new HashMap<>();
  public Object getController() {
    return null;
  }
	
  public BorderPane load(URL location, ResourceBundle resourceBundle) {
    BorderPane root = new BorderPane();
    {
      Button e_1 = new Button();
      e_1.setText("Hello World");
      root.setCenter(e_1);
    }
    return root;
  }
}

In the original code the only change I have to make is switch from FXMLLoader to a smarter class which first checks if there is a .class-File with the same name and create an instance of it.

ExtendedFXMLLoader loader = new ExtendedFXMLLoader();
loader.load(getClass().getClassLoader(),"Sample.fxml");

You can play with it yourself. I’ve uploaded a sample project to http://downloads.efxclipse.org/fxml-compiler.zip, give it a try on your own FXML-Files and if things fail file bugs against e(fx)clipse.

About these ads

26 Responses to “Convert FXML to Java as part of the build”

  1. Great job this is the so important feaure that was missing for FXML ! I hope that FXML will leverage MXML features.
    What about creating a maven plugin too ?

  2. Is it possible to download this tool by itself as a jar file?

    One thing I don’t like about fxml is that it needs all-permission to do the binding. Does generating the java class file prevent the need of the reflection API for the binding of the fxml to the controller?

    Thanks,

    • The sample project holds the tool jar. On reflection if fields and methods in your controller are public no reflection is used.

      • Tx, I’ll have a look.

      • Please note this alpha code so not all fxml files will translate and before i forget there’s still one reflection call needed

      • I’ve given it a try…

        I am trying to run the FXGraphCompiler directly but get an

        org.xml.sax.SAXException: javafx.geometry.Insets.getId();
        java.lang.NoSuchMethodException…

        Looking forward to see the progress you make on this project.

      • Ok so your inset has an id attribute? Can you show me the part of the fxml where the inset is defined?

      • HiTom,

        I deleted the id on the Insets in the fxml.

        The next problem I got was having a combobox which had three default items which the parse could not handle. I tried removing the fxcollections underneath but then I got another error which said something about parameter count.

        Maybe I can send you the fxml?

      • Sure tom dot schindl at bestsolution dot at

      • I have sent the file, if you do not receive it let me know.

        Thanks.

      • Received it but did not yet have time to take a look

  3. Looks great! A maven plugin would be great, if you need help let me know.

  4. where can i get the latest fxml-compiler.zip or its related files. which files are needed from efxclipse nitely build area to update your distribution?

  5. I got this exception while trying to bulild.
    JRE : 1.7.40
    Eclipse Kepler.

    Buildfile: D:\Jack\Workspaces\JavaFX\CompilerTest\build.xml
    test:
    [delete] Deleting directory D:\Jack\Workspaces\JavaFX\CompilerTest\build
    [mkdir] Created dir: D:\Jack\Workspaces\JavaFX\CompilerTest\build\classes
    [javac] D:\Jack\Workspaces\JavaFX\CompilerTest\build.xml:25: warning: ‘includeantruntime’ was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
    [javac] Compiling 5 source files to D:\Jack\Workspaces\JavaFX\CompilerTest\build\classes
    [fxml-compiler] Compiling D:\Jack\Workspaces\JavaFX\CompilerTest\src\demo\Login.fxml

    BUILD FAILED
    D:\Jack\Workspaces\JavaFX\CompilerTest\build.xml:27: java.lang.NoSuchMethodError: java.lang.reflect.Constructor.getParameterCount()I

    Total time: 5 seconds

  6. Hi there
    This is great work! However, the link to the nightly builds doesn’t work. Do you have another link?

Trackbacks/Pingbacks

  1. JavaFX links of the week, September 2 // JavaFX News, Demos and Insight // FX Experience - September 1, 2013

    […] Schindl has a blog about his progress on an FXML to Java converter, that removes the need to parse FXML files at runtime (which should prove beneficial on low-end […]

  2. Java desktop links of the week, September 2 « Jonathan Giles - September 1, 2013

    […] Schindl has a blog about his progress on an FXML to Java converter, that removes the need to parse FXML files at runtime (which should prove beneficial on low-end […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 665 other followers

%d bloggers like this: