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]]