Blog
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.