Code-behind class
You can combine FXML markup with a Java or Kotlin class (called a code-behind class), allowing you to combine declarative markup and imperative code. This can be done either with a standalone .fxml file plus a Java or Kotlin class, or with markup code embedded directly into Java or Kotlin source code by using the @ComponentView annotation.
FXML source file
In order to add a code-behind class to a FXML source file, create two files in the same package:
com/sample/MyControl.fxmlcom/sample/MyControl.java
In MyControl.fxml, add the fx:subclass attribute to the root element, and specify the fully-qualified name of the code-behind class.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.control.Button?>
<StackPane xmlns="http://javafx.com/javafx" xmlns:fx="http://jfxcore.org/fxml/2.0"
fx:subclass="com.sample.MyControl">
<Button fx:id="myButton1"/>
</StackPane>
In MyControl.java, create a new class that extends MyControlBase.
package com.sample;
public class MyControl extends MyControlBase {
public MyControl() {
initializeComponent();
myButton1.setText("Hello!");
}
}
Embedded markup
For embedded FXML markup, the code-behind class is the class being annotated with the @ComponentView annotation:
package com.sample;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import org.jfxcore.markup.ComponentView;
@ComponentView("""
<StackPane>
<Button fx:id="myButton"/>
</StackPane>
""")
public class MyControl extends MyControlBase {
public MyControl() {
initializeComponent();
myButton.setText("Hello!");
}
}
The
MyControlBaseclass is generated by the FXML compiler. It contains the compiled markup code, whether that markup comes from a standalone.fxmlfile or from a@ComponentViewannotation.If the generated class is missing for a standalone
.fxmlsource, run the:processFxmltask in your Gradle build to create the class. If it is missing for embedded markup, make sure that annotation processing is enabled in your build; see Enable annotation processing.By default, the name of the generated markup class corresponds to the name of the FXML file or the name of the class annotated with
@ComponentView, including theBasesuffix. This can be changed with thefx:classNameattribute.
Initializing the scene graph with initializeComponent
The scene graph specified in the FXML document is initialized by the compiler-generated initializeComponent() method. It is important that you call this method in the constructor of the code-behind class, as otherwise the components that make up the scene graph will never be instantiated.
You should call
initializeComponent()after you initialize all of the state which the FXML scene graph depends on, but before you actually access the FXML scene graph.
What is the relationship between fx:subclass, fx:controller, and fx:root?
fx:controller and fx:root are both directives of the FXML 1.0 format, and are not available in the FXML 2.0 format. fx:subclass is a new directive that allows combining markup and imperative code. This is similar to the fx:root directive, but doesn’t require using FXMLLoader to load the markup at runtime.
FXML 2.0 always compiles down to scene graph nodes; it does not support the markup/controller model of FXML 1.0 out of the box. Instead, it is left to developers to implement their favorite architectural patterns. FXML 2.0 is well-suited to implement patterns like MVC or MVVM.