Principes de disposition des IHM

Nous abordons ici un des aspects particuliers de la portabilité des codes: on ne peut concevoir un "écran" d’interaction avec l’utilisateur en faisant des hypothèses a priori sur les dimensions de cet écran … ni même sur le système de fenêtrage utilisé!

La conception des interactions doit donc suivre une philosophie particulière à laquelle il va falloir s’habituer!

[Avertissement]

Il est difficile pour le programmeur d’assumer ce découplage entre code et aspect graphique. La tentation est donc grande d’utiliser des outils interactifs pour le "design" d'écrans d’interaction.

Ce type d’outil est à manier avec le plus grand soin:

  • Trop de programmeurs se trouvent ainsi incités à réaliser des codes graphiques insuffisamment décomposés. On obtient alors de gros codes très difficile à maintenir!

    La difficulté est de bien décomposer les interactions en une série de codes spécialisés et ensuite de faire que ces composants soient pris en compte par l’outil d’aide au design.

  • Une partie de la difficulté avec ces gros codes vient du fait qu’ils sont bâtis sur des canevas générés par ces outils. La maintenance et les évolutions deviennent alors problématiques.
  • Les aspects visuels peuvent être trompeurs et lorsque le code s’exécute sur une machine ou un écran avec des caractéristiques différentes les effets recherchés peuvent donner des résultats inattendus.

Les librairies graphiques

Le défi pour Java a été de proposer des librairies graphiques qui puissent être portables sous différents systèmes de fenêtrage ("write once, run anywhere").

Attention: nous quittons ici les codes abrités dans le module par défaut java.base si vos propres codes sont dans un module il va falloir spécifier des directives requires.

AWT

Le package java.awt (et ses sous-packages) dans le module java.desktop

Ici les classes des composants graphiques sont réalisés à partir de composants natifs de la plate-forme locale ("peer classes"). Le graphique de base (dessin) utilise le même principe de code natif. (une autre librairie -non-standard- org.eclipse.swt est basée aussi sur le même principe)

Nous utiliserons ces codes dans un but pédagogique. Beaucoup de programmeurs trouvent ce niveau d’intervention pas assez riche pour des codes applicatifs.

SWING

package javax.swing (et ses sous-packages) dans le module java.desktop.

Ici les "widgets" graphiques sont réalisés avec du code Java qui s’appuie sur les primitives graphiques de bas niveau (les composants sont "dessinés").

La philosophie de disposition est, à peu de choses près, celle exposée dans AWT.

Pour des applications sophistiquées l’accent est mis maintenant sur JavaFX

JavaFX

Un ensemble de modules livrés avec javaSE : javafx.*

On trouvera là ce qui peut concerner des I.H.M. multimedia "riches".

La pratique de ces codes nécessite un apprentissage particulier qui ne sera pas abordé ici.

Autres bibliothèques

Il existe d’autre logithèques pour réaliser des interactions … par exemple celles d’Androïd.

Ici aussi il faudra suivre un cursus adapté (mais l’apprentissage des bases décrites ici donnera une expérience qui permettra d’aborder ces codes plus facilement).

images/orangebelt.png AWT est la librairie de base de référence; on y trouvera:

  • Des composants standard (Window, Panel, Button, Label,..) et des classes associées (Menu, CheckboxGroup,….)
  • Des "gestionnaires de disposition" ("Layout Manager") qui permettent de placer les composants les uns par rapport aux autres.
  • Des classes liées au graphique de bas niveau : Graphics, Graphics2D, Color, Font, Image,
  • Des "événements" et des gestionnaires de tâches graphiques: AWTEvent, MediaTracker, PrintJob,
  • Des structures de données: Point, Rectangle, Dimension,…

Composants et containers

Les composants graphiques sont des sous-clases de Component.

Certains d’entre eux sont des Containers: c’est à dire des composants capable de contenir d’autres composants.

Ici un composant Frame est un Container et un Button est un simple Component

containers and components

Un simple code graphique:

 public static void main(String[] args) {
        Frame frame = new Frame("BluFrame") ;
        frame.setBackground(Color.BLUE ) ;
        frame.setSize(200,200) ; // pas très politiquement correct
        frame.setVisible(true) ; // était show() avant 1.5

    }

ce code donne:

une fenêtre bleue

[Note]

L’exécution ne se termine pas quand on arrive à l’accolade fermante du main!

Une application Java se termine que lorsque les seuls Threads qui sont actifs sont des micro-tâches d’intendance ("daemon thread"). Ici bien que le Thread de nom main se termine il y a une autre micro-tâche applicative qui tourne: le Thread awt qui est en attente d’interactions avec l’utilisateur.

Disposition

Dans de nombreux langages de programmation la disposition des composants à l’intérieur d’un container graphique peut se faire en utilisant un positionnement par coordonnées absolues (en points, par rapport au coin supérieur gauche).

direct?

Bien que possible en Java ceci est mauvais pour la portabilité!

  • Les caractéristiques de la fenêtre englobante peuvent varier selon les systèmes de fenêtrage. (épaisseur du bandeau, …)
  • La taille des composants peut changer en fonction des fontes de caractères du système et de la langue du message! (la longueur d’un message peut varier!).

Les dispositions doivent être mises en place et controlées par un objet associé au Container et qui sert de "gestionnaire de disposition" (LayoutManager).

Gestionnaire de disposition FlowLayout

Voici ce qui se passe lorsqu’on associe un gestionnaire de disposition FlowLayout à un Container:

flowlayout 1

flowlayout 2

Les composants (Component) sont positionnés les uns derrière les autres; l’ordre d’invocation des méthodes add sur le Container est important.

Autant que possible chaque composant dans le container va conserver sa dimension "préférée".

Un code simple utilisant un FlowLayout

   public static void main(String[] args) {
        Frame frame = new Frame("FlowLayout") ;
        frame.setLayout(new FlowLayout(FlowLayout.LEADING)) ;
   // pourrait être : new FlowLayout()  ... centré

     frame.add(new Button("Sûr de sûr!")) ;
        frame.add(new Button("Ni pour ni contre (bien au contraire!)")) ;
        frame.add(new Button("Certainement pas!")) ;


        frame.pack() ;
        frame.setVisible(true) ;

    }

Gestionnaire de disposition BorderLayout

border 1

border 2

Un gestionnaire de disposition BorderLayout décrit des "zones" (nommées CENTER et les points cardinaux en anglais). Il y a seulement un seul composant positionné pour une zone donnée (on n’a pas besoin de "remplir" toutes les zones). La méthode add utilisée comporte un argument désignant la zone (l’ordre d’invocation des add est sans importance).

Quand le Container est redimensionné en expansion:

  • Le composant au centre est aggrandi autant que possible dans les deux directions (il ne conserve pas sa "taille préférée").
  • Les composants en NORTH et SOUTH gardent leur hauteur préférée
  • Les composants en EAST et WEST conservent leur largeur préférée.

Un code simple utilisant un BorderLayout

// package et imports

public class EnBorderLayout  extends Panel{
    private Button button1 = new Button("CENTER") ;
    private Button button2 = new Button("NORTH") ;
    private Button button3 = new Button("SOUTH") ;
    private Button button4 = new Button("EAST") ;
    private Button button5 = new Button("WEST") ;

    public EnBorderLayout() {
        // pourrait être: super(new BorderLayout()) ;
        this.setLayout(new BorderLayout());
        this.add(button1, BorderLayout.CENTER);
        this.add(button2, BorderLayout.PAGE_START); // NORTH
        this.add(button3, BorderLayout.PAGE_END); // SOUTH
        this.add(button4, BorderLayout.LINE_END); // EAST
        this.add(button5, BorderLayout.LINE_START); // WEST
    }

    public static void main(String[] args) {
        Frame frame = new Frame("BorderLayout") ;
        frame.add(new EnBorderLayout()) ;
        frame.pack() ;
        frame.setVisible(true) ;
    }

}

[Note]

BorderLayout est le gestionnaire par défaut des objets Frames (fenêtres "décorées").

[Note]Internationalisation

Vous avez du remarquer que dans le code on a utilisé des constantes nommées LINE_START plutôt que WEST. La raison en est qu’il faut prévoir l’internationalisation de nos I.H.M.

Mettre un composant "à l’Ouest" suppose que la lecture se fasse de gauche à droite … mais si le code s’exécute dans un contexte où la lecture de fait de droite à gauche alors le fait d’avoir indiqué LINE_START fera que la disposition s’adaptera automatiquement! (et votre composant se retrouvera à droite).

Voir la documentation de la classe java.awt.ComponentOrientation

Autres gestionnaires de disposition

GridLayout
Une grille dans laquelle toutes les "cellules" ont la même taille.
GridBagLayout
Une grille complexe avec des composants positionnés de diverses manières dans les cellules (trame en "tartan"). Certains composant peuvent recouvrir plusieurs cellules.
CardLayout
Mets les composants les uns au dessus des autres (un seul "au sommet" et des actions pour changer celui qui est sur le dessus).
GroupLayout
Les composants font partie de groupes qui facilitent les alignements. Ce gestionnaire est bien utile pour les outils interactifs de disposition (outils de programmation).

Combinaisons de dispositions

Pour construire une disposition complexe il faut savoir combiner différents Panels (ou des composants dérivés définis pas le programmeur). Ici, par exemple, dans la mesure où on ne peut pas mettre plus d’un composant dans une zone de BorderLayout on est obligé de mettre un Panel dans la zone NORTH puis d’ajouter d’autres composants dans ce Panel.

complex frame

Il vaut mieux éviter de construire un IHM basée sur une seule classe: un assemblage de différents composants d’IHM, ayant chacun un rôle bien défini, est une solution plus facile à maintenir et à réutiliser.

complex frame

Il faut insister sur le fait qu’il faut travailler les IHM non seulement en terme d’ergonomie (simplicité du design et des interactions + accueil des déficients visuels!) mais aussi en terme de programmation (séparation des fonctions, modularité).

images/bluebelt.png

[Note]Remarque sur l’assemblage de composants au moyen d’un outil

Il est possible d’utiliser des outils interactifs qui permettent de disposer des composants à l’intérieur d’un autre composant. Il est probable que les constructeurs de vos composants élémentaires exigent des paramètres or ceci est incompatible avec la phase de mise en place interactive de l’outil qui doit générer une image graphique déconnectée des contraintes fonctionnelles: la convention de *Bean* impose alors de définir un constructeur sans paramètre pour le composant graphique.

définition d’un constructeur conforme à la convention de Bean. 

public class MonComposant extends Panel {
   // code applicatif "normal"

   // code Bean : constructeur sans paramètres
   public MonComposant(){
      if(! java.beans.Beans.isDesignTime()) {
         throw new IllegalStateException(
            "use of default constructor out of design time");
      }
      // ....
   }

--exercices--