JavaFX

JavaFX is an open-source Java library to create graphical interfaces.

JavaFX's views are stored .fxml files usually created using Scene Builder 🛝 which is a WYSIWYG editor (What You See Is What You Get).

Common errors 🔥:

  • Graphics pipeline error: you are missing OS-specific files. For instance, javafx-base-xx-win.jar on Windows.

  • JavaFX runtime component missing: you are missing a vm option, refer to --module-path and --add-modules.

  • Can't load FXML error: the controller does not fit your FXML (duplicate IDs, mismatched type...)


Main

JavaFX's Main class must extend Application. This code below creates a Scene with a Label showing Hello, World!.

import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;

public class MainUI extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        Parent root = new Label("Hello, World!");
        Scene scene = new Scene(root, 800, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

Load FXML

Instead of creating root from the code, we usually load a FXML. This code may raise an IOException that must be handled!

// import javafx.fxml.FXMLLoader;
// import java.net.URL;
URL resource = MainUI.class.getResource("/main.fxml");
FXMLLoader loader = new FXMLLoader(resource);
Parent root = loader.load();

Load Controller

If you associated a controller to the FXML. You can get it back and call the init method (or whatever method you defined) using:

XXXController controller = loader.getController();
controller.init(primaryStage);

Components

Components can be layout managers or views. It means you can put a layout inside a layout, or a view otherwise.

Layout managers to organize components within the screen, such as:

  • 🐼 BorderPane: view split in five (North, South, East, West, Center)
  • 📚 VBox: items one below the other
  • 🚸 HBox: items one next to the other
  • ❄️ Pane: place components arbitrarily, not responsive
  • 🗃️ FlowPane: each component takes its preferred size
  • 🖍️ GridPane: a table (with cells, rows, and columns)
  • ...

Menus are handled separately. See MenuBar, Menu, and MenuItem. Use ContextMenu to customize the left-click menu.

Some commonly used views are:

  • Label: a text

  • ImageView: an image (use Tooltip to display a text on hover)

  • Button: a button

  • TextField/PasswordField: input fields

  • TextFlow: a group of Labels. Used to show a text with some labels having different sizes/colors/...

  • ListView: display a list of items

  • ...


FXML

FXML files are XML files with a syntax specific to JavaFX ☕. A starter FXML file using a BorderPane as the root could be:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane prefHeight="600" prefWidth="800" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1">
</BorderPane>

➡️ You usually don't edit the FXML manually, and use SceneBuilder instead. Some exceptions are copy-paste and buggy views.


SceneBuilder

Scene Builder is a Java editor to edit FXML files.

Scene Builder Main Frame

  1. Library 📚: a searchable list of components. You can drag and drop them inside "2" or "4".
  2. Hierarchy 🗃️: the tree representation of your XML. You can rename/move/duplicate/delete components from there.
  3. Controller 🚸: you can bind a controller here
  4. Scene 🖍️: you can preview your interface here. You can move components within the interface from here too.
  5. Inspector 👮‍♀️: when selecting a component (in "2" or "4"), you will be able to edit its properties here. (ex: change the background color)
  6. MenuBar 📂: the Preview menu is quite handy

The inspector is subdivided into three sections:

  • Properties: set a value/text, check/uncheck, show/hide, css...
  • Layout: to set margins, padding, size, alignment...
  • Code: to add a fxid or link a method from a controller

Controller

Each FXML file can have up to one controller. A controller is a class linking a view with the code. Add fx:controller to the root element of the FXML, either manually or using SceneBuilder.

<XXX ... fx:controller="xxx.XXXController" ...>
package xxx;

public class XXXController {
}

Calling a method when an event occurs 🎉

First, declare the method in your controller:

// all 4 are the SAME
@FXML private void onEventName(ActionEvent actionEvent) {}
@FXML private void onEventName() {}
public void onEventName(ActionEvent actionEvent) {}
public void onEventName() {}

Inside SceneBuilder, navigate to Inspector > Code. You can map an event such as onAction (click) to a method (e.g. onEventName).

Access a view from the code 📚

We may have to access a component from the code. For instance, to change a Label when the user clicks on a button. Navigate to the Code section of the Inspector. Give an ID to the target.

<Label fx:id="xxx" ... />

Inside the controller, add an attribute matching the given ID:

// both are the SAME
@FXML private BorderPane xxx;
public BorderPane xxx;

Then, inside every method aside from the constructor, your FXML attributes will be initialized with the associated component.

We usually add a method called init for post-JavaFX initializations:

public class XXXController {    
    public XXXController() {} // xxx is null
    public void init() {} // xxx won't be null
}

Style 🍔

Icon next to a text

Drag-and-Drop an image inside a Button/Label/... Select the label, and in the inspector, navigate to Properties:

  • Use Graphic Text Gap to add a gap between the image and the text
  • Use Content Display to move the image around. You can select GRAPHIC_ONLY or TEXT_ONLY to only show one of them.

Horizontal spacing

You can manually edit the FXML and add this between two views:

<HBox HBox.hgrow="ALWAYS" />

Localization

First, you will have to create a file for each language. For instance, i18n_en.properties for English, i18n_fr.properties for French...

Every file has the same keys, but different values.

# i18n_en.properties
msg=Hello, World!
# i18n_fr.properties
msg=Bonjour, Monde!

In your FXML, you should replace hardcoded texts with %key.

<Label text="%msg" />

You can also do it inside SceneBuilder. Hover the cog icon next to your text, select Replaced with internationalized string then enter msg:

cog icon

Loading a Locale

To load an FXML using texts from a language file, use:

// import java.util.Locale;
// import java.util.ResourceBundle;
Locale english = new Locale("en"); // xxx_en
ResourceBundle bundle = ResourceBundle.getBundle("i18n", english);
loader.setResources(bundle);

👻 To-do 👻

Stuff that I found, but never read/used yet.

  • School project
    • fx:include
  • lgs/eden
  • edencoding
  • valueProperty, selectedProperty, bind (uni/bi)