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.
- Library 📚: a searchable list of components. You can drag and drop them inside "2" or "4".
- Hierarchy 🗃️: the tree representation of your XML. You can rename/move/duplicate/delete components from there.
- Controller 🚸: you can bind a controller here
- Scene 🖍️: you can preview your interface here. You can move components within the interface from here too.
- Inspector 👮♀️: when selecting a component (in "2" or "4"), you will be able to edit its properties here. (ex: change the background color)
-
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 selectGRAPHIC_ONLY
orTEXT_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
:
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)