Styliser JavaFX avec CSS
JavaFX intègre un puissant support CSS qui permet de personnaliser l'apparence des composants de l'interface utilisateur. Cette fonctionnalité permet de séparer le style visuel de la structure et du comportement de l'application.
Introduction au CSS dans JavaFX
Le CSS (Cascading Style Sheets) dans JavaFX est similaire au CSS utilisé pour le web, mais avec quelques différences importantes :
- Les propriétés CSS commencent par
-fx-
pour les distinguer des propriétés CSS standard - JavaFX utilise un sous-ensemble de CSS3 avec des extensions spécifiques
- Les sélecteurs fonctionnent de manière similaire au CSS web
- Les feuilles de style peuvent être appliquées à l'ensemble de l'application, à une scène spécifique ou à des nœuds individuels
Avantages de l'utilisation du CSS dans JavaFX
- Séparation claire entre l'apparence et la logique
- Réutilisation des styles à travers l'application
- Possibilité de changer l'apparence sans modifier le code Java
- Facilité de création de thèmes (clair/sombre, etc.)
- Personnalisation plus fine que ce que permettent les API Java
Emplacement des fichiers CSS
Dans un projet Maven/Gradle standard, les fichiers CSS sont généralement placés dans le dossier des ressources :
src/
├── main/
│ ├── java/
│ │ └── com/
│ │ └── example/
│ │ └── javafxdemo/
│ │ └── MainApplication.java
│ └── resources/
│ └── com/
│ └── example/
│ └── javafxdemo/
│ ├── styles/
│ │ ├── application.css // Style principal
│ │ ├── dark-theme.css // Thème sombre
│ │ └── controls.css // Styles pour les contrôles
│ └── views/
│ └── main-view.fxml
Application des styles CSS
Il existe plusieurs façons d'appliquer des styles CSS à une application JavaFX :
1. Via le code Java
// Application à toute la scène
Scene scene = new Scene(root, 800, 600);
scene.getStylesheets().add(getClass().getResource("/com/example/javafxdemo/styles/application.css").toExternalForm());
// Application à un nœud spécifique
Button button = new Button("Cliquez-moi");
button.getStylesheets().add(getClass().getResource("/com/example/javafxdemo/styles/button-styles.css").toExternalForm());
// Styles inline (à éviter pour les applications complexes)
Label title = new Label("Titre principal");
title.setStyle("-fx-font-size: 24px; -fx-font-weight: bold; -fx-text-fill: #3366cc;");
2. Via FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<VBox xmlns:fx="http://javafx.com/fxml" fx:controller="com.example.javafxdemo.MainController">
<stylesheets>
<URL value="@../styles/application.css" />
<URL value="@../styles/controls.css" />
</stylesheets>
<children>
<Label text="Titre" styleClass="title-label" />
<Button text="Style par défaut" />
<Button text="Style personnalisé" styleClass="custom-button" />
<Button text="Style inline" style="-fx-background-color: #ff6666;" />
</children>
</VBox>
Sélecteurs CSS dans JavaFX
JavaFX prend en charge la plupart des sélecteurs CSS standards :
Type de sélecteur | Syntaxe | Exemple |
---|---|---|
Type de classe | .class-name |
.button - Tous les boutons |
ID | #id-name |
#submit-button - Élément avec fx:id="submit-button" |
Type d'élément | element-type |
Button - Tous les boutons |
Pseudo-classe | element:pseudo-class |
.button:hover - Boutons au survol |
Combinaison | parent > child |
.vbox > .button - Boutons enfants directs d'un VBox |
Exemple de fichier CSS pour JavaFX :
/* Styles pour tous les boutons */
.button {
-fx-background-color: #3498db;
-fx-text-fill: white;
-fx-font-weight: bold;
-fx-padding: 8px 16px;
-fx-cursor: hand;
}
/* État survol */
.button:hover {
-fx-background-color: #2980b9;
}
/* État pressé */
.button:pressed {
-fx-background-color: #1f618d;
}
/* Bouton de type danger */
.button.danger-button {
-fx-background-color: #e74c3c;
}
/* Bouton désactivé */
.button:disabled {
-fx-background-color: #bdc3c7;
-fx-opacity: 0.7;
}
/* Sélection par ID */
#main-title {
-fx-font-size: 24px;
-fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
-fx-font-weight: bold;
}
/* Sélecteur combiné */
.vbox > .label {
-fx-padding: 5px;
}
Propriétés CSS courantes dans JavaFX
Voici quelques-unes des propriétés CSS les plus utilisées dans JavaFX :
Texte et police
-fx-font-family: "Arial", sans-serif;
-fx-font-size: 14px;
-fx-font-weight: bold; /* normal, bold, 700 */
-fx-font-style: italic; /* normal, italic, oblique */
-fx-text-fill: #333333;
-fx-text-alignment: center; /* left, center, right, justify */
-fx-underline: true;
-fx-strikethrough: true;
Fond et bordures
-fx-background-color: #f5f5f5;
-fx-background-radius: 5px;
-fx-border-color: #cccccc;
-fx-border-width: 1px;
-fx-border-radius: 5px;
-fx-border-style: solid; /* solid, dotted, dashed */
Dimensions et espacement
-fx-pref-width: 200px;
-fx-pref-height: 30px;
-fx-min-width: 100px;
-fx-max-width: 300px;
-fx-padding: 10px; /* haut droite bas gauche: 10px 15px 10px 15px */
-fx-spacing: 10px; /* Pour VBox, HBox */
Effets
-fx-opacity: 0.8;
-fx-cursor: hand;
-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 10, 0, 0, 3);
Exemple complet : Style d'un formulaire
Voici un exemple complet d'un formulaire JavaFX stylisé avec CSS :
FXML (form-view.fxml)
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<GridPane xmlns:fx="http://javafx.com/fxml" fx:controller="com.example.javafxdemo.FormController"
styleClass="form-container" hgap="10" vgap="10">
<padding>
<Insets top="20" right="20" bottom="20" left="20"/>
</padding>
<stylesheets>
<URL value="@../styles/form-style.css"/>
</stylesheets>
<Label text="Formulaire d'inscription" styleClass="form-title"
GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.columnSpan="2"/>
<Label text="Nom :" GridPane.columnIndex="0" GridPane.rowIndex="1"/>
<TextField fx:id="nameField" promptText="Entrez votre nom"
GridPane.columnIndex="1" GridPane.rowIndex="1"/>
<Label text="Email :" GridPane.columnIndex="0" GridPane.rowIndex="2"/>
<TextField fx:id="emailField" promptText="Entrez votre email"
GridPane.columnIndex="1" GridPane.rowIndex="2"/>
<Label text="Mot de passe :" GridPane.columnIndex="0" GridPane.rowIndex="3"/>
<PasswordField fx:id="passwordField" promptText="Entrez votre mot de passe"
GridPane.columnIndex="1" GridPane.rowIndex="3"/>
<CheckBox fx:id="agreeCheckbox" text="J'accepte les conditions d'utilisation"
GridPane.columnIndex="0" GridPane.rowIndex="4" GridPane.columnSpan="2"/>
<HBox spacing="10" alignment="CENTER_RIGHT"
GridPane.columnIndex="0" GridPane.rowIndex="5" GridPane.columnSpan="2">
<Button text="Annuler" styleClass="cancel-button" onAction="#handleCancel"/>
<Button text="S'inscrire" styleClass="submit-button" onAction="#handleSubmit"/>
</HBox>
<Label fx:id="statusLabel" styleClass="status-label"
GridPane.columnIndex="0" GridPane.rowIndex="6" GridPane.columnSpan="2"/>
</GridPane>
Ce code va dans src/main/resources/com/example/javafxdemo/views/form-view.fxml
CSS (form-style.css)
/* Styles pour le formulaire */
.form-container {
-fx-background-color: white;
-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.1), 10, 0, 0, 5);
-fx-background-radius: 8px;
}
.form-title {
-fx-font-size: 20px;
-fx-font-weight: bold;
-fx-text-fill: #333333;
-fx-padding: 0 0 10px 0;
}
/* Styles pour les champs de saisie */
.text-field, .password-field {
-fx-pref-height: 35px;
-fx-background-color: #f5f5f5;
-fx-background-radius: 4px;
-fx-border-color: #e0e0e0;
-fx-border-radius: 4px;
-fx-padding: 0 10px;
-fx-font-size: 14px;
}
.text-field:focused, .password-field:focused {
-fx-background-color: white;
-fx-border-color: #3498db;
-fx-effect: dropshadow(three-pass-box, rgba(52, 152, 219, 0.2), 5, 0, 0, 0);
}
/* Styles pour les boutons */
.button {
-fx-background-radius: 4px;
-fx-padding: 8px 15px;
-fx-font-weight: bold;
-fx-cursor: hand;
}
.submit-button {
-fx-background-color: #2ecc71;
-fx-text-fill: white;
}
.submit-button:hover {
-fx-background-color: #27ae60;
}
.submit-button:pressed {
-fx-background-color: #229954;
}
.cancel-button {
-fx-background-color: transparent;
-fx-text-fill: #7f8c8d;
-fx-border-color: #bdc3c7;
-fx-border-radius: 4px;
}
.cancel-button:hover {
-fx-background-color: #ecf0f1;
}
/* Style pour la case à cocher */
.check-box {
-fx-padding: 10px 0;
}
.check-box .box {
-fx-background-color: white;
-fx-border-color: #bdc3c7;
-fx-border-radius: 3px;
}
.check-box:selected .mark {
-fx-background-color: white;
}
.check-box:selected .box {
-fx-background-color: #3498db;
}
/* Style pour l'étiquette de statut */
.status-label {
-fx-padding: 10px 0 0 0;
-fx-font-style: italic;
}
.status-success {
-fx-text-fill: #2ecc71;
}
.status-error {
-fx-text-fill: #e74c3c;
}
Ce code va dans src/main/resources/com/example/javafxdemo/styles/form-style.css
Contrôleur (FormController.java)
package com.example.javafxdemo;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
public class FormController {
@FXML private TextField nameField;
@FXML private TextField emailField;
@FXML private PasswordField passwordField;
@FXML private CheckBox agreeCheckbox;
@FXML private Label statusLabel;
@FXML
private void handleSubmit() {
if (!validateForm()) {
return;
}
// Traitement du formulaire
statusLabel.setText("Inscription réussie !");
statusLabel.getStyleClass().remove("status-error");
statusLabel.getStyleClass().add("status-success");
}
@FXML
private void handleCancel() {
nameField.clear();
emailField.clear();
passwordField.clear();
agreeCheckbox.setSelected(false);
statusLabel.setText("");
}
private boolean validateForm() {
// Validation simple
if (nameField.getText().trim().isEmpty()) {
showError("Le nom est requis");
return false;
}
if (emailField.getText().trim().isEmpty() || !emailField.getText().contains("@")) {
showError("Email invalide");
return false;
}
if (passwordField.getText().length() < 6) {
showError("Le mot de passe doit contenir au moins 6 caractères");
return false;
}
if (!agreeCheckbox.isSelected()) {
showError("Vous devez accepter les conditions d'utilisation");
return false;
}
return true;
}
private void showError(String message) {
statusLabel.setText(message);
statusLabel.getStyleClass().remove("status-success");
statusLabel.getStyleClass().add("status-error");
}
}
Ce code va dans src/main/java/com/example/javafxdemo/FormController.java
Thèmes clair/sombre
Une application courante du CSS dans JavaFX est la création de thèmes clair et sombre que l'utilisateur peut changer à la volée.
Style commun (common.css)
/* Variables communes */
* {
-fx-font-family: "Segoe UI", Arial, sans-serif;
}
/* Classes utilitaires */
.container {
-fx-padding: 20px;
}
/* Styles de base pour les boutons */
.button {
-fx-padding: 8px 15px;
-fx-background-radius: 4px;
-fx-cursor: hand;
}
Ce code va dans src/main/resources/com/example/javafxdemo/styles/common.css
Thème clair (light-theme.css)
/* Thème clair */
.root {
-fx-base: #ececec;
-fx-background: #f5f5f5;
-fx-control-inner-background: white;
-fx-text-fill: #333333;
-fx-accent: #3498db;
}
.title {
-fx-text-fill: #2c3e50;
}
.label {
-fx-text-fill: #333333;
}
.button {
-fx-background-color: #3498db;
-fx-text-fill: white;
}
.button:hover {
-fx-background-color: #2980b9;
}
.text-field {
-fx-background-color: white;
-fx-border-color: #bdc3c7;
}
Ce code va dans src/main/resources/com/example/javafxdemo/styles/light-theme.css
Thème sombre (dark-theme.css)
/* Thème sombre */
.root {
-fx-base: #2c3e50;
-fx-background: #1a1a1a;
-fx-control-inner-background: #2c3e50;
-fx-text-fill: #ecf0f1;
-fx-accent: #3498db;
}
.title {
-fx-text-fill: #ecf0f1;
}
.label {
-fx-text-fill: #ecf0f1;
}
.button {
-fx-background-color: #3498db;
-fx-text-fill: white;
}
.button:hover {
-fx-background-color: #2980b9;
}
.text-field {
-fx-background-color: #34495e;
-fx-border-color: #2c3e50;
-fx-text-fill: #ecf0f1;
}
Ce code va dans src/main/resources/com/example/javafxdemo/styles/dark-theme.css
Application du thème dans le code Java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ThemeApplication extends Application {
private Scene scene;
private boolean isDarkTheme = false;
@Override
public void start(Stage primaryStage) throws Exception {
// Chargement de la vue principale
FXMLLoader loader = new FXMLLoader(getClass().getResource("views/main-view.fxml"));
Parent root = loader.load();
// Création de la scène avec le thème par défaut (clair)
scene = new Scene(root, 800, 600);
scene.getStylesheets().add(getClass().getResource("styles/common.css").toExternalForm());
scene.getStylesheets().add(getClass().getResource("styles/light-theme.css").toExternalForm());
// Bouton pour changer de thème (ajouté directement dans le code pour l'exemple)
Button themeToggle = new Button("Basculer en mode sombre");
themeToggle.setOnAction(e -> toggleTheme(themeToggle));
// Ajout du bouton à la vue
((VBox) root).getChildren().add(0, themeToggle);
// Configuration et affichage
primaryStage.setTitle("Application avec thèmes");
primaryStage.setScene(scene);
primaryStage.show();
}
private void toggleTheme(Button themeToggle) {
// Suppression du thème actuel
scene.getStylesheets().remove(1);
if (isDarkTheme) {
// Passage au thème clair
scene.getStylesheets().add(getClass().getResource("styles/light-theme.css").toExternalForm());
themeToggle.setText("Basculer en mode sombre");
} else {
// Passage au thème sombre
scene.getStylesheets().add(getClass().getResource("styles/dark-theme.css").toExternalForm());
themeToggle.setText("Basculer en mode clair");
}
isDarkTheme = !isDarkTheme;
}
public static void main(String[] args) {
launch(args);
}
}
Ce code va dans src/main/java/com/example/javafxdemo/ThemeApplication.java
Bonnes pratiques avec CSS dans JavaFX
- Utiliser des variables CSS pour définir une palette de couleurs cohérente
- Structurer les feuilles de style de manière logique (séparation par thème, par type de contrôle, etc.)
- Éviter le style inline autant que possible
- Préférer les classes CSS aux sélecteurs d'ID pour les styles réutilisables
- Commenter le code CSS pour expliquer les styles complexes
- Rester cohérent dans les noms de classes et les conventions de style
- Tester sur différentes tailles d'écran pour s'assurer que l'interface s'adapte correctement
Exemple de variables CSS pour une palette cohérente
/* Définition des variables de couleur */
.root {
/* Couleurs primaires */
-fx-color-primary: #3498db;
-fx-color-primary-dark: #2980b9;
-fx-color-primary-light: #a9cce3;
/* Couleurs secondaires */
-fx-color-secondary: #2ecc71;
-fx-color-secondary-dark: #27ae60;
/* Couleurs d'alerte */
-fx-color-success: #2ecc71;
-fx-color-warning: #f39c12;
-fx-color-danger: #e74c3c;
/* Couleurs neutres */
-fx-color-text: #333333;
-fx-color-text-light: #7f8c8d;
-fx-color-background: #f5f5f5;
-fx-color-border: #bdc3c7;
}
/* Utilisation des variables */
.button.primary {
-fx-background-color: -fx-color-primary;
-fx-text-fill: white;
}
.button.primary:hover {
-fx-background-color: -fx-color-primary-dark;
}
.label.error {
-fx-text-fill: -fx-color-danger;
}