2.1Comprendre FXML
2.1.1 – Rôle et structure d'un fichier FXML
FXML (JavaFX Markup Language) est un format XML qui permet de décrire l'interface utilisateur d'une application JavaFX de manière déclarative. Au lieu d'écrire du code Java pour créer chaque composant, vous pouvez utiliser FXML pour définir la structure de votre interface de façon plus lisible et maintenable.
🎯 Qu'est-ce que FXML ?
FXML est un langage de balisage basé sur XML qui permet de :
- Décrire la structure de l'interface : Définir quels composants sont présents et comment ils sont organisés
- Séparer la vue de la logique : L'interface est dans le FXML, la logique est dans le contrôleur Java
- Faciliter la maintenance : Modifier l'interface sans toucher au code Java
- Permettre l'édition visuelle : Utiliser Scene Builder pour créer et modifier l'interface graphiquement
📄 Structure de base d'un fichier FXML
Un fichier FXML minimal contient les éléments suivants :
1. En-tête XML
<?xml version="1.0" encoding="UTF-8"?>
Cette ligne indique que le fichier est un document XML en UTF-8.
2. Imports des classes JavaFX
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
Les directives <?import ... ?> permettent d'importer les classes JavaFX que vous allez utiliser dans le fichier. C'est l'équivalent des import en Java.
Button, vous devez ajouter <?import javafx.scene.control.Button?>.
3. Élément racine
<VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml/1">
<!-- Contenu ici -->
</VBox>
L'élément racine est le conteneur principal de votre interface. Il peut être un layout comme VBox, HBox, BorderPane, etc. Les attributs xmlns définissent les espaces de noms XML nécessaires.
📋 Exemple complet d'un fichier FXML minimal
Voici un exemple complet d'un fichier FXML simple qui affiche un Label :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Label text="Hello JavaFX" />
</children>
</VBox>
•
<?xml ... ?> : Déclaration XML•
<?import ... ?> : Import des classes JavaFX nécessaires•
<VBox> : Conteneur racine qui organise les éléments verticalement•
<children> : Liste des éléments enfants du VBox•
<Label text="Hello JavaFX" /> : Un Label avec le texte "Hello JavaFX"
🔄 Comparaison : FXML vs Code Java
Pour mieux comprendre FXML, comparons la même interface créée de deux manières différentes :
Version Java (code impératif)
package application;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class MonApplication extends Application {
@Override
public void start(Stage primaryStage) {
// Créer le conteneur
VBox root = new VBox();
// Créer le Label
Label label = new Label("Hello JavaFX");
// Ajouter le Label au conteneur
root.getChildren().add(label);
// Créer la Scene
Scene scene = new Scene(root, 400, 300);
// Afficher
primaryStage.setScene(scene);
primaryStage.setTitle("Mon Application");
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Version FXML (déclarative)
Fichier main.fxml :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Label text="Hello JavaFX" />
</children>
</VBox>
Fichier Main.java (chargement du FXML) :
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
// Charger le fichier FXML
VBox root = FXMLLoader.load(getClass().getResource("main.fxml"));
// Créer la Scene
Scene scene = new Scene(root, 400, 300);
// Afficher
primaryStage.setScene(scene);
primaryStage.setTitle("Mon Application");
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
📁 Où placer les fichiers FXML ?
Dans un projet Eclipse JavaFX créé avec e(fx)clipse, vous avez deux options pour placer vos fichiers FXML :
Option 1 : Dans le package application (structure par défaut)
Par défaut, e(fx)clipse place les fichiers directement dans le package application :
src/
└── application/
├── Main.java
├── main.fxml # Fichier FXML ici
└── style.css # Fichier CSS ici
Pour charger le FXML depuis Java, utilisez :
FXMLLoader.load(getClass().getResource("main.fxml"));
Option 2 : Dans un dossier resources (organisation recommandée)
Pour une meilleure organisation, vous pouvez créer un dossier resources dans src et le configurer comme Source Folder :
src/
├── application/
│ └── Main.java
└── resources/ # Dossier à créer manuellement
└── main.fxml # Fichier FXML ici
Configuration importante : Pour que cette structure fonctionne, vous devez configurer le dossier resources comme Source Folder dans Eclipse :
- Créez le dossier
resourcesdanssrc - Dans Eclipse, faites un clic droit sur le dossier
resources - Sélectionnez Build Path → Use as Source Folder
- Le dossier apparaîtra alors comme un Source Folder dans votre projet
Pour charger le FXML depuis Java, utilisez :
FXMLLoader.load(getClass().getResource("/main.fxml"));
/ au début du chemin indique que le fichier est à la racine du classpath. Une fois resources configuré comme Source Folder, il devient partie du classpath, donc /main.fxml cherche dans resources/main.fxml. Si votre fichier FXML est dans un sous-dossier, par exemple src/resources/vues/accueil.fxml, utilisez getResource("/vues/accueil.fxml").
2.1.2 – Avantages du FXML vs code Java
Utiliser FXML pour créer vos interfaces JavaFX offre de nombreux avantages par rapport à la création purement en code Java. Cette section explore ces avantages en détail.
🎨 Séparation de la vue et de la logique
Le principal avantage de FXML est la séparation claire entre la présentation (vue) et la logique métier :
- FXML : Contient uniquement la description de l'interface (quels composants, comment ils sont organisés)
- Contrôleur Java : Contient la logique (que faire quand on clique sur un bouton, comment traiter les données)
Si vous voulez changer la couleur d'un bouton, vous modifiez le FXML ou le CSS, pas le code Java. Si vous voulez changer ce qui se passe quand on clique, vous modifiez le contrôleur Java, pas le FXML.
🔄 Réutilisabilité
Un fichier FXML peut être réutilisé dans différentes parties de votre application :
- Composants réutilisables : Créez un composant personnalisé dans un FXML et réutilisez-le plusieurs fois
- Modularité : Divisez votre interface en plusieurs fichiers FXML plus petits et gérables
- Chargement dynamique : Chargez différents FXML selon le contexte de l'application
🛠️ Maintenance facilitée
FXML rend la maintenance de votre code plus simple :
- Modifications visuelles sans recompilation : Modifiez le FXML et rechargez l'application sans recompiler le code Java
- Code Java plus propre : Le code Java se concentre sur la logique, pas sur la création de composants
- Lisibilité : La structure hiérarchique du FXML est plus facile à comprendre que du code Java imbriqué
👁️ Édition visuelle avec Scene Builder
FXML peut être édité visuellement avec Scene Builder :
- Glisser-déposer : Ajoutez des composants en les glissant depuis la palette
- Prévisualisation en temps réel : Voyez immédiatement le résultat de vos modifications
- Configuration des propriétés : Modifiez les propriétés des composants via une interface graphique
- Pas besoin de connaître le code : Les designers peuvent créer des interfaces sans écrire de code
📊 Comparaison détaillée
Voici un tableau comparatif entre FXML et code Java pur :
| Critère | Code Java | FXML |
|---|---|---|
| Séparation vue/logique | Mélangée dans le code | Séparée (FXML + contrôleur) |
| Édition visuelle | Non (code uniquement) | Oui (Scene Builder) |
| Lisibilité | Code Java imbriqué | Structure XML hiérarchique |
| Maintenance | Modifications dans le code | Modifications dans FXML |
| Réutilisabilité | Code à dupliquer | Fichier FXML réutilisable |
| Collaboration | Développeurs uniquement | Développeurs + designers |
💡 Quand utiliser FXML vs Code Java ?
Bien que FXML soit recommandé pour la plupart des cas, il y a des situations où le code Java peut être préférable :
✅ Utilisez FXML pour :
- La structure principale de l'interface
- Les écrans complets de l'application
- Les composants réutilisables
- Quand vous voulez utiliser Scene Builder
✅ Utilisez le code Java pour :
- Les interfaces très simples (une fenêtre avec un Label)
- Les composants générés dynamiquement (créés en boucle)
- Les prototypes rapides
- Quand la structure change beaucoup selon les données
📝 Exemple comparatif complet
Voici le même formulaire de connexion créé de deux manières différentes :
Version Java (code impératif)
VBox root = new VBox(10);
root.setPadding(new Insets(20));
root.setAlignment(Pos.CENTER);
Label titre = new Label("Connexion");
titre.setFont(new Font("Arial", 24));
titre.setTextFill(Color.DARKBLUE);
TextField username = new TextField();
username.setPromptText("Nom d'utilisateur");
username.setPrefWidth(200);
PasswordField password = new PasswordField();
password.setPromptText("Mot de passe");
password.setPrefWidth(200);
Button btnConnexion = new Button("Se connecter");
btnConnexion.setPrefWidth(200);
btnConnexion.setOnAction(e -> {
// Logique de connexion
});
root.getChildren().addAll(titre, username, password, btnConnexion);
Version FXML (déclarative)
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.*?>
<VBox spacing="10" alignment="CENTER" xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml/1">
<padding>
<Insets top="20" right="20" bottom="20" left="20"/>
</padding>
<children>
<Label text="Connexion" style="-fx-font-size: 24px; -fx-text-fill: darkblue;"/>
<TextField promptText="Nom d'utilisateur" prefWidth="200"/>
<PasswordField promptText="Mot de passe" prefWidth="200"/>
<Button text="Se connecter" prefWidth="200" onAction="#handleConnexion"/>
</children>
</VBox>
• Plus lisible et structuré
• Peut être édité visuellement dans Scene Builder
• Séparation claire entre la vue et la logique (la méthode
handleConnexion est dans le contrôleur)• Plus facile à modifier sans toucher au code Java
2.1.3 – Déclaration du contrôleur (fx:controller)
Pour que votre fichier FXML puisse interagir avec le code Java, vous devez associer un contrôleur. Le contrôleur est une classe Java qui gère les événements et la logique de votre interface.
🎯 Qu'est-ce qu'un contrôleur ?
Un contrôleur est une classe Java qui :
- Gère les événements : Répond aux clics de boutons, aux saisies dans les champs, etc.
- Accède aux composants : Lit et modifie les valeurs des composants de l'interface
- Contient la logique métier : Traite les données, effectue les calculs, appelle les services
📝 Déclarer un contrôleur dans FXML
Pour associer un contrôleur à votre fichier FXML, utilisez l'attribut fx:controller sur l'élément racine :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="application.MonController">
<children>
<Label fx:id="monLabel" text="Hello" />
<Button fx:id="monBouton" text="Cliquer" onAction="#handleClick" />
</children>
</VBox>
•
fx:controller="application.MonController" : Spécifie le nom complet de la classe contrôleur•
fx:id="monLabel" : Donne un identifiant au Label pour pouvoir y accéder dans le contrôleur•
onAction="#handleClick" : Spécifie la méthode à appeler quand on clique sur le bouton
💻 Créer la classe contrôleur
La classe contrôleur doit respecter certaines conventions :
- Package : Doit correspondre au package spécifié dans
fx:controller - Annotations @FXML : Utilisez
@FXMLpour marquer les champs et méthodes liés au FXML - Méthode initialize() : Optionnelle, appelée automatiquement après le chargement du FXML
Exemple de contrôleur simple
package application;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.Button;
public class MonController {
// Référence au Label dans le FXML (fx:id="monLabel")
@FXML
private Label monLabel;
// Référence au Button dans le FXML (fx:id="monBouton")
@FXML
private Button monBouton;
// Méthode appelée automatiquement après le chargement du FXML
@FXML
private void initialize() {
// Code d'initialisation ici
monLabel.setText("Initialisé !");
}
// Méthode appelée quand on clique sur le bouton (onAction="#handleClick")
@FXML
private void handleClick() {
monLabel.setText("Clic détecté !");
System.out.println("Le bouton a été cliqué");
}
}
handleClick, handleSubmit). C'est une convention, pas une obligation.
🔗 Liaison FXML ↔ Contrôleur
La liaison entre le FXML et le contrôleur se fait de plusieurs manières :
1. Liaison par fx:id
Utilisez fx:id dans le FXML et un champ annoté @FXML dans le contrôleur :
Dans le FXML :
<Label fx:id="monLabel" text="Hello" />
<TextField fx:id="champNom" />
<Button fx:id="btnValider" text="Valider" />
Dans le contrôleur :
@FXML
private Label monLabel;
@FXML
private TextField champNom;
@FXML
private Button btnValider;
fx:id dans le FXML. JavaFX fait la correspondance automatiquement.
2. Liaison des événements
Utilisez onAction (ou autres attributs d'événement) dans le FXML et une méthode annotée @FXML dans le contrôleur :
Dans le FXML :
<Button text="Cliquer" onAction="#handleClick" />
<TextField onKeyPressed="#handleKeyPress" />
Dans le contrôleur :
@FXML
private void handleClick() {
// Code exécuté quand on clique sur le bouton
}
@FXML
private void handleKeyPress(KeyEvent event) {
// Code exécuté quand on appuie sur une touche
}
onAction (sans le #). Par exemple, onAction="#handleClick" appelle la méthode handleClick().
📋 Exemple complet : Application avec contrôleur
Voici un exemple complet d'une application simple avec FXML et contrôleur :
1. Fichier main.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox spacing="20" alignment="CENTER" xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="application.MonController">
<padding>
<Insets top="20" right="20" bottom="20" left="20"/>
</padding>
<children>
<Label fx:id="labelMessage" text="Bienvenue !" />
<TextField fx:id="champTexte" promptText="Entrez votre nom" />
<Button fx:id="btnValider" text="Valider" onAction="#handleValider" />
</children>
</VBox>
2. Fichier MonController.java
package application;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.Button;
public class MonController {
@FXML
private Label labelMessage;
@FXML
private TextField champTexte;
@FXML
private Button btnValider;
@FXML
private void initialize() {
// Initialisation : désactiver le bouton au départ
btnValider.setDisable(true);
// Activer le bouton quand on tape dans le champ
champTexte.textProperty().addListener((obs, oldVal, newVal) -> {
btnValider.setDisable(newVal.trim().isEmpty());
});
}
@FXML
private void handleValider() {
String nom = champTexte.getText();
if (!nom.trim().isEmpty()) {
labelMessage.setText("Bonjour, " + nom + " !");
champTexte.clear();
}
}
}
3. Fichier Main.java (chargement du FXML)
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
// Charger le fichier FXML
FXMLLoader loader = new FXMLLoader(getClass().getResource("main.fxml"));
VBox root = loader.load();
// Créer la Scene
Scene scene = new Scene(root, 400, 300);
// Afficher
primaryStage.setScene(scene);
primaryStage.setTitle("Mon Application");
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
1. L'application charge le fichier
main.fxml2. JavaFX crée automatiquement une instance de
MonController3. Les champs annotés
@FXML sont remplis avec les références aux composants4. La méthode
initialize() est appelée automatiquement5. Quand on clique sur "Valider", la méthode
handleValider() est appelée
⚠️ Erreurs courantes
Voici les erreurs les plus courantes lors de l'utilisation de contrôleurs :
1. Nom de package incorrect
<!-- ❌ Erreur : package incorrect -->
<VBox fx:controller="monpackage.MonController">
<!-- ✅ Correct -->
<VBox fx:controller="application.MonController">
2. fx:id ne correspond pas au nom du champ
<!-- FXML -->
<Label fx:id="monLabel" />
// ❌ Erreur : nom différent
@FXML
private Label label;
// ✅ Correct
@FXML
private Label monLabel;
3. Méthode d'événement introuvable
<!-- FXML -->
<Button onAction="#handleClick" />
// ❌ Erreur : méthode non annotée @FXML ou nom incorrect
private void click() { }
// ✅ Correct
@FXML
private void handleClick() { }
• Le package dans
fx:controller correspond au package de votre classe• Les
fx:id correspondent exactement aux noms des champs• Les méthodes d'événement sont annotées
@FXML et ont le bon nom