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.fxml
  • com/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.

com/sample/MyControl.fxml
<?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.

com/sample/MyControl.java
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 MyControlBase class is generated by the FXML compiler. It contains the compiled markup code, whether that markup comes from a standalone .fxml file or from a @ComponentView annotation.

If the generated class is missing for a standalone .fxml source, run the :processFxml task 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 the Base suffix. This can be changed with the fx:className attribute.

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.


This site uses Just the Docs, a documentation theme for Jekyll.