1- Premier exemple simple : HelloWorld

Pour créer une application JavaFX avec IntelliJ IDEA, il faut sélectionner dans le menu un nouveau projet (File -> new -> project). Vous obtiendrez la fenêtre suivante : 

Capture décran 2022 03 26 à 10.05.02 

Vous pouvez constater que dans la liste à gauche de la fenêtre, on peut choisir JavaFX comme type de projet. Mais pour les TPs, je vous déconseille de choisir cette option car à partir de la version 2021.2 d'Idea, cette option vous oblige à utiliser un système de production comme Maven ou Gradle. La présentation de tel système est hors de la portée de ce cours. Pour se passer de ces systèmes, choisissez Java comme type de projet. Vous pouvez ensuite choisir le SDK qui sera utilisé pour ce projet. Si vous choisissez une version égale ou supérieure à 11, il faut télécharger javaFX à partir de cette page. Pour éviter les problèmes d'incompatibilité, la version de javaFX doit être la même que la version du SDK java.  Si vous choisissez une version de SDK inférieure à 11, javaFX est déjà inclu dans le sdk et vous n'avez rien à télécharger. Par contre, je vous conseille d'utiliser une version récente comme vous l'avez déjà fait avec le projet de la SAE 126. 

Ensuite, il faut appuyer sur le bouton next deux fois et vous aurez une fenêtre dans laquelle vous devez donner un nom à votre projet : 

Capture décran 2022 03 26 à 10.31.19

Le projet sera créé mais il ne contiendra pas de fichier sources. Pour développer un premier exemple simple, créez une classe appelée HelloWorld et copiez le code suivant dans cette classe :

public class HelloWorld extends Application {
public static void main(String[] args) {
launch(args);
}

@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!"); //titre de la fenêtre
Label label = new Label("Hello World"); //créer un label
StackPane root = new StackPane(); //créer un conteneur
root.getChildren().add(label); //ajouter le bouton au conteneur
primaryStage.setScene(new Scene(root, 300, 250)); //mettre le conteneur comme contenu de la fenêtre.
primaryStage.show();
}
}

Cet exemple définit la classe HelloWorld qui est la classe principale de l'application JavaFX et qui est une sous classe de javafx.application.Application. Cette classe redéfinit la méthode start() qui est le point d'entrée de l'application JavaFX. La méthode start() prend en paramètre la fenêtre principale primaryStage. Une scène est associée à la fenêtre et contiendra les composants graphiques de cette fenêtre. Des gestionnaires de positionnement appelés Layout sont utilisés pour placer les composants graphiques dans la scène. La méthode start() suivante crée une fenêtre qui a le titre Hello World et qui affiche aussi le label Hello World

Dans ce premier exemple, la fenêtre principale (primaryStage) est affichée. Le contenu d'une fenêtre est défini dans un objet de la classe Scene qui est associé à cette dernière. Un objet Scene utilise des gestionnaires de positionnement (Layouts) pour positionner les composants graphiques dans le contenu de la fenêtre. Dans ce premier exemple, le layout StackPane a été utilisé. Plus d'information sur les layouts seront données dans les sections suivantes.

Si vous avez choisi un SDK égal ou supérieur à 11, l'éditeur ne va pas reconnaitre les classes de la bibliothèque javaFX. Pour régler ce problème, sélectionnez dans le menu File -> project structure. Puis dans l'élément Librairies, ajoutez en appuyant sur le bouton + la bibliothèque javaFX que vous avez téléchargée. Il faut choisir le répertoire lib de la bibliothèque javaFX. L'ajout de cette bibliothèque est affiché à la droite de la fenêtre comme suit : 

Capture décran 2022 03 26 à 10.43.28

Ensuite, il faut importer toutes les classes utilisées de la bibliothèque javaFX. Faites bien attention, de ne pas importer ces classes de la bibliothèque awt qui contient des composants ayant les mêmes noms.

Si vous exécutez cet exemple avec un SDK égal ou supérieur à 11, vous aurez cette erreur : Error: JavaFX runtime components are missing, and are required to run this application. Pour régler ce problème, créez une configuration d'exécution et ajoutez les options de VM suivantes :

--module-path /path/to/javafx-sdk-17.0.1/lib --add-modules javafx.controls,javafx.fxml

Faites attention, à la place /path/to/.../lib vous mettez le chemin vers le répertoire lib de la bibliothèque javaFX que vous avez téléchargé comme dans la figure suivante :

Capture décran 2022 03 26 à 11.10.44

 

L'image ci-dessous est la fenêtre obtenue après la compilation et l'exécution de ce premier exemple. 

first_example

 

 L'interface graphique peut être représentée par un arbre appelé scene graph. Par exemple, la figure suivante présente le scene graph de l'application "Hello World" où tous les éléments JavaFX sont représentés par des noeuds dans le graphe : 

Capture décran 2022 03 16 à 15.34.52

2- Les différents composants graphiques :

La majorité des composants graphiques qu'on peut ajouter au contenu d'une fenêtre sont des sous classes de la classe Control du package javafx.scene.control et se trouvent dans ce même package.
Cette section présente brièvement quelques composants graphiques. Cependant, vous ne trouverez ici que des exemples d'utilisation basique de ces composants. Chaque composant a en réalité bien plus de paramètres. Pour avoir une description complète, veuillez consulter la documentation en ligne : l'API Java.

2-1- Label et TextField :

1
2
Label labNom=new Label("Nom");
TextField tfNom=new TextField();

label-textfield
2-2- Button et RadioButton :

1
2
3
4
5
6
7
ToggleGroup group = new ToggleGroup();
RadioButton rbM=new RadioButton("Male");
rbM.setToggleGroup(group);
rbM.setSelected(true);
RadioButton rbF=new RadioButton("Femelle");
rbF.setToggleGroup(group);
Button button = new Button("OK");

 

button-radioButton



2-3- CheckBox et ComboBox :

1
2
3
4
ComboBox combo=new ComboBox(FXCollections
                .observableArrayList(new String[]{"IE","Chrome", "Firefox" }));
combo.getSelectionModel().select(0);
CheckBox cb=new CheckBox("J'accepte les conditions d'utilisations");

 

combobox-checkbox



2-4- Menu :

1
2
3
4
5
6
7
8
9
10
11
1
Menu file=new Menu("File");
MenuItem open=new MenuItem("Open");
MenuItem newFile=new MenuItem("New File");
Menu save=new Menu("Save as");
MenuItem pdf=new MenuItem(".pdf");
MenuItem txt=new MenuItem(".txt");
save.getItems().addAll(pdf,txt);
MenuItem close=new MenuItem("Close");
file.getItems().addAll(open,newFile,save,close);

MenuBar menuBar=new MenuBar();
menuBar.getMenus().add(file);
BorderPane border=new BorderPane();
border.setTop(menuBar);

 

Menu

 

3- Les Gestionnaires de placement (Layout managers) :

Le rôle d'un gestionnaire de placement est de positionner les composants graphiques dans la fenêtre. Ci-dessous quelques exemples de Layout managers prédéfinis dans JavaFX.


3-1- FlowPane : 

Avec un FlowPane, l'équivalent du FlowLayout en Swing, les composants sont ajoutés les uns après les autres sur la même ligne et quand il n'y a plus de place sur la ligne courante les composants sont ajoutés sur la ligne suivante. Voir le code et l'image ci-dessous.

1
2
3
4
5
6
7
8
9
10
11
1
FlowPane flow = new FlowPane();
flow.setPadding(new Insets(10, 5, 10, 5)); //Ajouter des espaces entre le layout et la fenêtre
flow.setVgap(4); //Ajouter des espaces entre les lignes de composants
flow.setHgap(4); //Ajouter des espaces entre les composants dans une même ligne
flow.setStyle("-fx-background-color: DAE6F3;");

Button b1 = new Button("Button One");
Button b2 = new Button("Button Two");
Button b3 = new Button("Button Three");
Button b4 = new Button("Button Four");
Button b5 = new Button("Button Five");
Button b6 = new Button("Button Six");
Button b7 = new Button("Button Seven");
flow.getChildren().addAll(b1,b2,b3,b4,b5,b6,b7);

 

flowLayout


3-2 BorderPane :

Le conteneur est divisé en 5 zones : nord, sud, est ouest, et le centre. Il ne peut donc pas contenir plus de 5 composants. Les composants sont placés en fonction de leur taille préférée et leur alignement peut être modifié avec la méthode setAlignement(). Si aucun composant n'est associé à une zone, alors cette zone est de taille nulle. Les composant sont séparés horizontalement et verticalement par des espaces, qui peuvent être modifiés par la méthode setMargin() :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
BorderPane border = new BorderPane();
Button b1 = new Button("TOP");
Button b2 = new Button("LEFT");
Button b3 = new Button("CENTER");
Button b4 = new Button("RIGHT");
Button b5 = new Button("BOTTOM");
BorderPane.setAlignment(b1,Pos.CENTER);
border.setMargin(b1,new Insets(10, 10, 10, 10));
BorderPane.setAlignment(b2,Pos.TOP_CENTER);
border.setMargin(b2,new Insets(10, 10, 10, 10));
BorderPane.setAlignment(b3,Pos.CENTER);
border.setMargin(b3,new Insets(10, 10, 10, 10));
BorderPane.setAlignment(b4,Pos.BOTTOM_CENTER);
border.setMargin(b4,new Insets(10, 10, 10, 10));
BorderPane.setAlignment(b5,Pos.CENTER);
border.setMargin(b5,new Insets(10, 10, 10, 10));

border.setTop(b1);
border.setLeft(b2);
border.setCenter(b3);
border.setRight(b4);
border.setBottom(b5);

 

BorderPane


3-3- HBox et VBox :

HBox et VBox positionnent les composants les uns après les autres horizontalement ou verticalement. La méthode setSpacing() peut être utilisée pour spécifier l'espace entre les composants.


1
2
3
4
5
6
7
8
9
1
Button b1 = new Button("One");
Button b2 = new Button("Two");
Button b3 = new Button("Three");
Button b4 = new Button("Four");
Button b5 = new Button("Five");

HBox hbox=new HBox();
hbox.setSpacing(10);
hbox.setPadding(new Insets(10,10,10,10));
hbox.getChildren().addAll(b1,b2,b3,b4,b5);

hbox        vbox

3-4- GridPane :

Les composants sont placés dans un tableau à deux dimensions. La taille de chaque cellule est adaptée aux dimensions du composant qu'elle contient. Les composant sont séparés horizontalement et verticalement par des espaces, qui peuvent être modifiés par les méthodes setHgap(int g) et setVgap(int g). Il est possible de spécifier dans quelles colonne et ligne un composant doit être ajouté, voir le code ci-dessous.

1
2
3
4
5
6
7
8
9
10
11
12
13
1
Button b1 = new Button("One");
Button b2 = new Button("Two");
Button b3 = new Button("Three");
Button b4 = new Button("Four");
Button b5 = new Button("Five");
Button b6 = new Button("Six");

GridPane grid = new GridPane();
grid.setHgap(5);
grid.setVgap(5);
grid.setPadding(new Insets(5, 10, 5, 10));

grid.add(b1, 0, 2);
grid.add(b2, 0, 0);
grid.add(b3, 1, 0);
grid.add(b4, 2, 0);
grid.add(b5, 0, 1);
grid.add(b6, 1, 1);

 

 

 

GridPane


4- CSS :

Avec JavaFX, les instructions CSS peuvent être utilisées pour la mise en forme des composants graphiques. Pour ajouter une feuille de style au contenu d'une fenêtre, il faut utiliser l'instruction suivante : 

scene.getStylesheets().add("stylesheet.css");


Comme avec les éléments HTML, dans une feuille de style CSS il est possible de sélectionner un composant graphique en lui fixant un ID avec la méthode setId() et puis en CSS avec "#ID".
Il est possible d'ajouter des composants à une classe avec getStyleClass().add() et les sélectionner avec ".class".
Enfin, tous les composants du même type peuvent être sélectionnés par ".type".
Une fois les composants sont sélectionnés, leurs propriétés peuvent être modifiées entre accolades et séparées par des ";".
Dans le code JavaFX, la méthode setStyle() peut être aussi utilisée pour appliquer directement une instruction CSS sur un composant graphique.
Voir ci-dessous le code JavaFx, la feuille de style CSS appliquée sur ses composants et le résultat de l'application du CSS.


1
2
3
4
5
6
7
8
9
10
11
Button b1 = new Button("One");
b1.setStyle("-fx-background-color: gray;"); //Application directe d'une instruction CSS
Button b2 = new Button("Two");
Button b3 = new Button("Three");
Button b4 = new Button("Four");
Button b5 = new Button("Five");
b4.getStyleClass().add("blueButton"); //Ajouter b4 à la classe blueButton
b5.getStyleClass().add("blueButton"); //Ajouter b5 à la classe blueButton
Button b6 = new Button("Six");
b6.setId("uniqueButton");    //Associer l'ID uniqueButton à b6

Scene scene=new Scene(grid,200,200);
scene.getStylesheets().add("stylesheet.css"); //Associer la feuille de style au contenu de la fenêtre

 

Feuille de style :

1
2
3
4
5
6
7
8
9
10
11
12
13

.button{
    -fx-background-radius: 60;
    -fx-background-color: greenyellow;
    -fx-text-fill: red;
    -fx-font-weight: bold;
    -fx-font-family: sans-serif;
    -fx-border-radius: 60;
    -fx-border-color: black;
}

.blueButton{
    -fx-background-color: blue;
}

#uniqueButton{
    -fx-background-color: mediumpurple;
}

 

CSS

 

 5- Dialog

Les Dialogs peuvent être utilisées pour afficher des messages dans des petites fenêtres pour les utilisateurs comme dans la fenêtre suivante : 

Capture décran 2022 03 16 à 15.53.07

Cette fenêtre peut être obtenue avec le code suivant : 

Alert alert = new Alert(AlertType.INFORMATION);
alert.setTitle("Information Dialog");
alert.setHeaderText("Look, an Information Dialog");
alert.setContentText("I have a great message for you!");
alert.showAndWait();

Pour changer l'icône de la fenêtre, il faut utiliser un autre type d'alert à la place de AlertType.INFORMATION, par exemple :  AlertType.ERROR, AlertType.WARNING, etc.

 

La fenêtre peut demander à l'utilisateur de confirmer son choix :

 Capture décran 2022 03 16 à 16.19.42

 

Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Confirmation Dialog");
alert.setHeaderText(null);
alert.setContentText("Are you sure you want to save this file?");
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK){ // ... user chose OK } else { // ... user chose CANCEL or closed the dialog }

 

Personnaliser les boutons :

Capture décran 2022 03 16 à 16.50.04

 

Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Confirmation Dialog");
alert.setHeaderText(null);
alert.setContentText("Are you sure you want to save this file?");
ButtonType buttonTypeSavePDF = new ButtonType("save as .pdf");
ButtonType buttonTypeSaveTXT = new ButtonType("save as .txt");
ButtonType buttonTypeCancel = new ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE);
alert.getButtonTypes().setAll(buttonTypeSavePDF, buttonTypeSaveTXT, buttonTypeCancel);
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == buttonTypeSavePDF){
// ... user chose "save pdf"
} else if (result.get() == buttonTypeSaveTXT) {
// ... user chose "save txt"
} else {
// ... user chose CANCEL or closed the dialog
}

 

Fenêtre avec champs de texte : 

Capture décran 2022 03 16 à 17.37.09  

TextInputDialog dialog = new TextInputDialog("Jean");
dialog.setTitle("Text Input Dialog");
dialog.setHeaderText(null);
dialog.setContentText("Please enter your name:");
Optional<String> result = dialog.showAndWait();
if (result.isPresent()){
System.out.println("Your name: " + result.get());
}

 

Fenêtre avec liste déroulante : 

Capture décran 2022 03 16 à 17.44.00

List<String> choices = new ArrayList<>();
choices.add("Edge");
choices.add("Safari");
choices.add("Chrome");

ChoiceDialog<String> dialog = new ChoiceDialog<>("Edge", choices);
dialog.setTitle("Choice Dialog");
dialog.setHeaderText(null);
dialog.setContentText("Choose a browser:");
Optional<String> result = dialog.showAndWait();
if (result.isPresent()){
System.out.println("Your have chosen : " + result.get());
}

 

Personnaliser la fenêtre  et le type de retour :

Capture décran 2022 03 16 à 18.26.07

Dialog<Pair<String, String>> dialog = new Dialog<>();
dialog.setTitle("Login Dialog");
dialog.setHeaderText(null);
// Set the button types.
ButtonType loginButtonType = new ButtonType("Login", ButtonBar.ButtonData.OK_DONE);
dialog.getDialogPane().getButtonTypes().addAll(loginButtonType, ButtonType.CANCEL);

// Create the username and password labels and fields.
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(20, 10, 10, 10));

TextField username = new TextField();
username.setPromptText("Username");
PasswordField password = new PasswordField();
password.setPromptText("Password");

grid.add(new Label("Username:"), 0, 0);
grid.add(username, 1, 0);
grid.add(new Label("Password:"), 0, 1);
grid.add(password, 1, 1);

// Enable/Disable login button depending on whether a username was entered.
Node loginButton = dialog.getDialogPane().lookupButton(loginButtonType);
loginButton.setDisable(true);
username.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
loginButton.setDisable(newValue.trim().isEmpty());
}
});

dialog.getDialogPane().setContent(grid);

// Convert the result to a username-password-pair when the login button is clicked.
dialog.setResultConverter(new Callback<ButtonType,Pair<String,String>>(){
@Override
public Pair<String,String> call(ButtonType dialogButton) {
if (dialogButton == loginButtonType) {
return new Pair<String,String>(username.getText(), password.getText());
}
return null;
}
});

Optional<Pair<String, String>> result = dialog.showAndWait();
if (result.isPresent())
System.out.println("Username=" + result.get().getKey() + ", Password=" + result.get().getValue());