Cours BUT Info

Ch. 1 — JavaFX : widgets et placement

Ce qu'est un système interactif, la plateforme JavaFX (Stage, Scene, graphe de scène), les familles de widgets et les gestionnaires de placement — avec les quatre tailles d'un nœud et le redimensionnement.

Un système interactif

« Un système interactif est un système qui prend en compte, pendant son exécution, des informations communiquées par un ou plusieurs utilisateurs, et qui fournit, pendant son exécution, une représentation perceptible de son état interne. »

Ce n'est donc pas un système algorithmique : son fonctionnement dépend d'informations externes qu'il ne contrôle pas. Le paradoxe de l'IHM : beaucoup de systèmes semblent « tomber en marche », alors que la conception de l'interface peut absorber jusqu'à 80 % du temps de développement et conditionne le succès ou l'échec du produit. L'interface idéale ne demanderait aucun apprentissage, permettrait de réaliser toutes ses tâches rapidement et sans erreur avec peu d'effort, et satisferait ses utilisateurs. Comme cet idéal n'existe pas, tout est affaire de compromis pouvoir d'expression / simplicité, résumé par la devise d'A. Kay : « simple things should be simple, complex things should be possible ».

De AWT à JavaFX

Trois générations de bibliothèques graphiques se succèdent :

  • AWT (Abstract Window Toolkit) : les composants utilisent des ressources propres au système — l'apparence diffère d'une plateforme à l'autre et les fonctionnalités restent rudimentaires ;
  • Swing : offre beaucoup plus de composants, affichés de façon identique quel que soit le système (le Look-and-feel configure la représentation visuelle) ; ses classes commencent par la lettre J ;
  • JavaFX : le successeur de Swing, inclus dans Java 8 en 2014, puis module séparé à partir de Java 11 (2018).

Stage, Scene et point d'entrée

Le point d'entrée d'une application JavaFX est une classe qui hérite de Application. La méthode launch effectue les travaux préparatoires, puis appelle start en lui transmettant la fenêtre — le Stage — créée par l'application :

public class HelloJavaFX extends Application {
    @Override public void start(Stage stage) {
        stage.setTitle("Hello JavaFX Application");
        stage.show();   // toujours à la fin de start
    }
    public static void main(String[] args) {
        Application.launch(args);
    }
}

Le Stage contient une Scene, qui contient les widgets de la fenêtre :

Button bouton = new Button("Bouton");
Scene scene = new Scene(bouton, 300, 50);
stage.setScene(scene);

Le widget et ses tâches élémentaires

Un widget est un objet graphique interactif — on parle de control en JavaFX. Bouton, potentiomètre, case à cocher, champ texte et boîtes de fichiers existaient dès le Macintosh de 1984 ; menus hiérarchiques, listes, combo box et onglets sont venus ensuite. Les tâches élémentaires d'interaction se classent en familles :

  • Saisie : de texte (TextField, TextArea), de quantités (Slider, Spinner), de positions (pointage), de tracés ;
  • Sélection d'un ou plusieurs éléments dans un ensemble : cardinal fixe (menu, boutons radio, cases à cocher) ou variable (liste, ComboBox, pointage) ; menus déroulants, pop-up ou palettes, organisés hiérarchiquement ou circulairement ; le RangeSlider sélectionne une plage de valeurs — de 13 à 24 secondes pour trouver un film dans une liste de 10 000 ;
  • Déclenchement : boutons et menus, clic-tirer (le drag-and-drop, où l'action dépend de la source et de la destination), entrée gestuelle ;
  • Défilement (ScrollPane) et spécification d'arguments et de propriétés : boîtes de dialogue modales ou non, boîtes à onglets, boîtes de propriétés à effet immédiat.

Fenêtre modale

Une fenêtre B est modale par rapport à une fenêtre A si l'affichage de B empêche l'accès — et non la vue — à la fenêtre A. En JavaFX : stage2.initOwner(stage) puis stage2.initModality(Modality.WINDOW_MODAL).

Le graphe de scène

Le graphe de scène structure la représentation spatiale de l'interface sous forme d'arbre : chaque nœud a plusieurs enfants mais un seul parent ; il possède une racine — celle qu'on passe à la Scene ; ses feuilles sont des widgets, ses nœuds internes des conteneurs, qui peuvent contenir widgets et conteneurs. Dans un conteneur, les enfants sont stockés dans une ObservableList<Node>, accessible par getChildren(), qui propose add et addAll — le varargs Java, un seul paramètre variable par méthode, toujours en dernier.

Les quatre tailles d'un nœud

Chaque nœud possède quatre tailles : la taille idéale (preferred size), la taille minimale, la taille maximale et la taille réelle. Un layout essaie de rendre la taille réelle la plus proche possible de la taille préférée, compte tenu de sa stratégie et de la taille du conteneur. Un widget ne grandit jamais au-delà de son maximum : pour étirer un bouton sur toute la place offerte, on écrit top.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE).

Les gestionnaires de placement

Un conteneur possède un gestionnaire de placement (layout manager) qui calcule la position et les dimensions de chacun de ses enfants suivant une stratégie donnée. La question à se poser en permanence : que se passe-t-il quand on agrandit la fenêtre ?

  • Pane : positionnement absolu — bouton1.relocate(30, 20) — origine au coin supérieur gauche ;
  • BorderPane : cinq zones top, left, right, bottom, center (via setTop…) ; il satisfait la plupart des applications de bureau ;
  • FlowPane : les nœuds les uns à côté des autres ou les uns en dessous des autres, la ligne étant coupée quand elle est pleine ;
  • HBox et VBox : une ligne, une colonne :
HBox hb = new HBox();
hb.setSpacing(3);                       // espace entre les widgets
hb.setAlignment(Pos.CENTER_LEFT);       // alignement des widgets
hb.setPadding(new Insets(3, 3, 3, 3));  // espace autour de la HBox
hb.getChildren().addAll(lbl_nom, tf_nom, bt_ok, bt_cancel);
HBox.setHgrow(tf_nom, Priority.ALWAYS); // méthode statique

setHgrow contrôle la priorité d'agrandissement d'un nœud quand la fenêtre s'élargit — croissance bornée par la taille maximale. Pour coller un bouton au bord droit, on insère une Region vide comme espaceur et on lui donne le Hgrow : c'est elle qui absorbe l'excédent.

  • TilePane : disposition en grille dont toutes les cellules ont la même taille ;
  • GridPane : un nœud peut occuper plusieurs cellules — gp.add(b, 1, 1, 1, 2) place le bouton en 2e colonne, 2e ligne, sur 1 colonne et 2 lignes — avec setHgap/setVgap et setGridLinesVisible(true) pour visualiser la grille ;
  • ToolBar, TabPane (onglets via getTabs().addAll(...)) et SplitPane (séparateur, setDividerPositions(0.3)) n'héritent pas de Pane ; dans un SplitPane, chaque item doit être un gestionnaire de placement pour que le séparateur soit manipulable.

Avant de coder, dessinez le graphe

Pour reproduire une interface — et la faire tenir au redimensionnement — dessinez d'abord son graphe de scène : widgets, conteneurs, gestionnaires, et qui grandit. Le cours mentionne enfin JavaFX Scene Builder, un générateur d'interfaces.

A retenir

  • une interface = un graphe de scène : widgets en feuilles, conteneurs en nœuds internes, racine posée dans la Scene, fenêtre = Stage ;
  • chaque conteneur applique une stratégie de placement : BorderPane, HBox/VBox, FlowPane, TilePane, GridPane, ToolBar, TabPane, SplitPane ;
  • chaque nœud a quatre tailles ; le layout rapproche la taille réelle de la taille préférée, jamais au-delà du maximum ;
  • le comportement au redimensionnement se décide dans le graphe : setHgrow, Region espaceur, setMaxSize.

R2.02 - Cours 1 - Introduction - widgets (partie 1).pdf

R2.02 · Cours 1 — Introduction et widgets (1/2) · 31 diapositives

Consultation seule — téléchargement désactivé

Chargement du document…

R2.02 - Cours 1 - Introduction - gestionnaires de placement (partie 2).pdf

R2.02 · Cours 1 — Gestionnaires de placement (2/2) · 37 diapositives

Consultation seule — téléchargement désactivé

Chargement du document…

Dernière mise à jour le

Sur cette page