Bibliothèque Java pour coder des **interfaces graphiques** (GUI) de bureau (mais aussi mobile et embarqué).
On peut même considérer que c'est **un framework** puisqu'on code à l'intérieur de celui-ci.
On parle d'application "clients riches".
Beaucoup de **composants graphiques** disponibles.
Peut-être stylées avec un dialecte de CSS.
Interfaces graphiques peuvent être construites avec un markup FXML.
Interactivité sur un modèles d'événements.
## Outils autour de JavaFx
- documentation : https://openjfx.io/openjfx-docs/
- Construire des scenes visuellement avec Scene Builder : https://gluonhq.com/products/scene-builder
- Tests automatiques d'interfaces : https://github.com/TestFX/TestFX/
<!--
### Installer
https://gluonhq.com/products/javafx/
Choisir le **SDK** pour votre plateforme
![[Capture d’écran 2026-03-03 à 10.17.38.png]]
### Linux
Vous devez avoir téléchargé une fichier .zip.
- Le décompresser
- Vous devriez obtenir un dossier `javafx-sdk-25.0.2` (il peut également être un sous-dossier)
Depuis l'endroit ou le zip a été décompressé
```sh
# Créer un dossier de destination
mkdir -p $HOME/javafx
# déplacer javafx-sdk-25.0.2 dans ce dossier
mv javafx-sdk-25.0.2 $HOME/javafx
```
Ensuite nous allons exporter une variable d'environnement pour indiquer à programme Java ou trouver ce dossier.
Avec un éditeur de texte, ouvrez le fichier `.bashrc` présent dans votre home.
Ajouter la ligne suivante
```sh
export PATH_TO_FX="$HOME/javafx/javafx-sdk-25.0.2"
```
### Windows
### macOS
-->
## Activité - votre hello world en javafx
### Créer un nouveau projet Maven
### Installer les 2 dépendances de javafx via maven
```xml
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>21.0.6</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>21.0.6</version>
</dependency>
</dependencies>
```
## Créer un package
Créer un package nommé `school.coda.javafx`
![[image-40.png]]
## Ajouter `module-info.java`
Dans `src/main/java`
Ajouter un fichier `module-info.java`
![[image-38.png]]
Modifier le `module-info.java`
```java
module school.coda.javafx {
requires javafx.controls;
requires javafx.fxml;
opens school.coda.javafx to javafx.graphics, javafx.fxml;
}
```
### Recopier le code suivant
Via API programmatique
```java
package school.coda.javafx;
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class HelloApplication extends Application {
@Override
public void start(Stage stage) {
Text text = new Text("Hello World!");
Parent root = new StackPane(text);
Scene scene = new Scene(root, 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
// Lanceur
static void main(String[] args) {
Application.launch(HelloApplication.class, args);
}
}
```
## Votre projet devrait ressembler à ceci
![[image-39.png]]
### Exécuter le programme `main`
Vous devriez obtenir le résultat suivant.
![[javafx-hello-world.png|219x183]]
## Activité - Une interface un peu plus élaborée
Copier-coller le code suivant
```java
package school.coda.javafx;
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class HelloApplication extends Application {
@Override
public void start(Stage stage) {
// Des composants
Text text = new Text("Hello World!");
Button button = new Button("Button");
// Un élément de mise en page contient les éléments
// pour les aligner verticalement
VBox vBox = new VBox(text, button);
// Application d'un style CSS pour center l'élément
// de mise en page
vBox.setStyle("-fx-alignment: center");
// Ajout du nœud dans un élément parent
Parent root = new StackPane(vBox);
// Ajout de l'élément parent dans une scène
Scene scene = new Scene(root, 320, 240);
// Configuration du stage
stage.setTitle("Hello!");
// Ajout de la scène au stage
stage.setScene(scene);
stage.show();
}
// Lanceur
static void main(String[] args) {
Application.launch(HelloApplication.class, args);
}
}
```
### Résultat attendu
![[fxml-example-centered.png|219]]
### Explications
Dans la suite de ce cours, nous allons expliquer le code qu'on a copié-collé.
## Comment se structure une application JavaFx
- Stage : la fenêtre de l'application
- Scene : un stage doit contenir une Scène. Celle-ci contient tous les éléments de l'application
- Node : Un graphe d'élements graphiques (conteneurs ou éléments d'UI) qui peuvent eux-même contenir d'autres noeuds.
```mermaid
flowchart TD
subgraph Stage
subgraph Scene
subgraph Nodes [Scene graph]
direction TB
root[Root]
branch[Branch]
leaf1[Leaf]
leaf2[Leaf]
leaf3[Leaf]
root --> branch
root --> leaf1
branch --> leaf2
branch --> leaf3
end
end
end
```
L'exemple précédent vu sous la forme de diagramme
```mermaid
flowchart TD
subgraph Stage
subgraph Scene
subgraph Nodes [Scene graph]
direction TB
root[StackPane]
vbox[VBox]
text[Text]
button[Button]
root --> vbox
vbox --> text
vbox --> button
end
end
end
```
> [!tip] Design Pattern !
> Le système de graphe de Node est implémenté à l'aide du [Design Pattern Composite](https://refactoring.guru/fr/design-patterns/composite)
## Composants
Les composants font partie d'une hiérarchie de classe dont voici quelques éléments.
- **Node**
- **Shape** : formes (lignes, rectangle, texte ...)
- **Region**
- **Pane** : éléments de mise en page
- **Control** : zone de formulaire et interactions
- **ImageView** : images et multimédia
- **Canvas** : zone d'image sur laquelle on peut dessiner programmatiquement
```mermaid
classDiagram
direction BT
class Node
class Scene
class Parent
class ImageView
class Canvas
class Region
class Control
class Label
class Button
class CheckBox
class TextArea
class Pane
class StackPane
class FlowPane
class HBox
class VBox
class Shape
class Text
Pane --> Parent
Parent --> Node
Canvas --> Node
Shape --> Node
ImageView --> Node
Region --> Node
Control --> Region
Label --> Control
Button --> Control
CheckBox --> Control
TextArea --> Control
Pane --> Region
FlowPane --> Pane
HBox --> Pane
StackPane --> Pane
VBox --> Pane
Line --> Shape
Circle --> Shape
Ellipse --> Shape
Text --> Shape
Stage "1" *-- Scene
Scene "1" *-- Parent
Pane *-- "*" Node
```
## Controles
Les contrôles sont les composants permettant de saisir du texte, les boutons...
### Exemples de controles disponibles
- Accordion
- Button
- CheckBox
- ChoiceBox
- ColorPicker
- ComboBox
- DatePicker
- Label
- ListView
- Menu
- MenuBar
- PasswordField
- ProgressBar
- RadioButton
- Slider
- Spinner
- SplitMenuButton
- SplitPane
- TableView
- TabPane
- TextArea
- TextField
- TitledPane
- ToggleButton
- ToolBar
- TreeTableView
- TreeView
## Eléments de mise en page
![[javafx-containers.png|319x189]]
Des composant conteneurs pour mettre en page.
### Exemples de conteneurs
- Group
- Region
- Pane
- HBox
- VBox
- FlowPane
- BorderPane
- BorderPane
- StackPane
- TilePane
- GridPane
- AnchorPane
- TextFlow
### Ajouter des éléments à un conteneur
```java
StackPane root = new StackPane();
root.getChildren().add(new Button());
```
## Evénements
L'application peut réagir à des événements.
Ex.
- Appui sur un bouton
- Clavier
- Souris
- ...
Les `Node` peuvent écouter des événements et déclencher du code quand ils surviennent.
On attache des "event listeners" : des fonctions qui sont appelées à la survenue d'un événement.
Par `Node::addEventHandler()`
Ou par une méthode pré-existante
ex.
- `setOn...()`
- `setOnMouseClicked()`
```java
button.setOnMouseClicked((MouseEvent event) -> /* Do someting */);
button.addEventHandler(ActionEvent.ANY, (ActionEvent event) -> /* Do someting */);
```
## Types d'événements
```mermaid
flowchart LR
event_any[Event.ANY]
input_event_any[InputEvent.ANY]
key_event_any[KeyEvent.ANY]
key_event_pressed[KeyEvent.PRESSESD]
key_event_released[KeyEvent.RELEASED]
action_event_any[ActionEvent.ANY]
window_event_any[WindowEvent.ANY]
window_event_showing[WindowEvent.SHOWING]
window_event_shown[WindowEvent.SHOWN]
mouse_event_any[MouseEvent.ANY]
event_any --- input_event_any
input_event_any --- key_event_any
key_event_any --- key_event_pressed
key_event_any --- key_event_released
input_event_any --- mouse_event_any
event_any --- action_event_any
event_any --- window_event_any
window_event_any --- window_event_showing
window_event_any --- window_event_shown
```
## Différentes façons d'ajouter un event handler
Expression lambda
```java
button.addEventHandler(ActionEvent.ANY, (ActionEvent event) -> /* Do someting */);
```
Référence de méthode statique
```java
button.setOnMouseClicked(HelloApplication::doSomething);
```
```java
private static void doSomething(MouseEvent event) {
/* do something */
}
```
Classe anonyme
```java
button.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
/* Do something*/
}
});
```
Classe externe nommée
```java
class DoSomethingMouseEventHandler implements EventHandler<MouseEvent> {
@Override
public void handle(MouseEvent event) {
/* Do something*/
}
}
```
## Properties
Dans JavaFx, les properties sont des valeurs réactives.
Cela signifie que quand elles changent, leur changement peut être automatiquement propagé à d'autres objets qui les observent.
Dans l'exemple suivant, on associe la propriété du texte du label à celle du texte du textField.
Explications dans les paragraphes qui suivent.
```java
package fr.baldir.javafx.properties;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class JavafxPropertiesApplication extends Application {
// Lanceur
static void main(String[] args) {
Application.launch(JavafxPropertiesApplication.class, args);
}
@Override
public void start(Stage stage) {
TextField textField = new TextField("Texte initial");
Label label = new Label();
// Je connecte les 2 propriétés de texte
label.textProperty().bind(textField.textProperty());
// Une facon d'aligner alternative à CSS
VBox vBox = new VBox(textField, label);
vBox.setAlignment(Pos.CENTER);
stage.setScene(new Scene(new StackPane(vBox),320,240));
stage.setTitle("Hello!");
stage.show();
}
}
```
![[tp-javafx-reactive-properties.mp4]]
### Quelques types de propriétés
- `IntegerProperty`
- `BooleanProperty`
- `DoubleProperty`
- `StringProperty`
- Et bien d'autres sur le même modèle ! ...
```java
IntegerProperty intProp = new SimpleIntegerProperty();
BooleanProperty booleanProp = new SimpleBooleanProperty();
DoubleProperty doubleProp = new SimpleDoubleProperty();
StringProperty stringProp = new SimpleStringProperty();
```
### Ecrire une propriété
`maProperty.set( nouvelleValeur )`
```java
StringProperty prop1 = new SimpleStringProperty();
prop1.set("Valeur set depuis prop1");
```
### Lire une propriété
`maProperty.get()`
```java
StringProperty prop1 = new SimpleStringProperty();
String valeur = prop1.get()
```
### Associer une propriété bidirectionnelle
`prop2.bindBidirectional(prop1);`
```java
StringProperty prop1 = new SimpleStringProperty();
prop1.set("Valeur set depuis prop1");
IO.println("prop1.get() : "+prop1.get());
// "Valeur set depuis prop1"
StringProperty prop2 = new SimpleStringProperty();
// Associer les 2 valeurs
prop2.bindBidirectional(prop1);
IO.println("prop2.get() : "+prop2.get());
// "Valeur set depuis prop1"
prop2.set("Valeur set depuis prop2");
IO.println("prop1.get() : "+prop1.get());
// "Valeur set depuis prop2"
```
## À suivre
[[FXML]]