3 bis Cours ENPC, printemps 2000
Java, objets, interfaces et Internet

Swing

Swing ne remplace pas AWT. Il continue à utiliser les classes Color, Graphics, Toolkit, Font, FontMetrics et les classes de placement de l'AWT, par contre il n'utilise pas ses composants.

Presque tous les composants Swing sont 100% java. Seuls les conteneurs racines font exception à la règle : JFrame, JApplet, JWindow et JDialog. L'aspect et le comportement de l'interface graphique deviennent indépendants du système d'exploitation.

Swing fournit des composants qui viennent en remplacement des composants AWT et en ajoute de nouveaux. Au total, Swing fournit plus de 40 composants.

Composants 100% Java

Les composants Swing sont définis dans le paquet javax.swing et ses sous-paquets javax.swing.text pour les composants gérant du texte et javax.swing.table pour ce qui concerne les tableaux.

Tous les composants ci-dessous dérivent de la classe JComponent et sont 100% Java. Comme JComponent étend java.awt.Container, ce sont aussi tous des conteneurs.

Éléments décoratifs

Pour décorer les composants, Swing dispose de la classe ImageIcon et des classes qui dérivent de la classe AbstractBorder.

Un objet de type ImageIcon permet de charger une image au format GIF ou JPEG à partir d'un fichier ou d'une URL. L'icône obtenue peut ensuite être ajoutée à un JLabel, par exemple :

ImageIcon joeIcon = new ImageIcon("./joeCaml.gif");
JLabel joeLabel = new JLabel(joeIcon);

Il existe 8 types de cadres prédéfinis :

  • BevelBorder : cadre 3D qui peut être en creux ou en bosse
  • CompoundBorder : cadre composite formé de plusieurs cadres élémentaires
  • EmptyBorder : cadre vide, sert à réserver de la place
  • EtchedBorder : cadre ayant la forme d'un sillon
  • LineBorder : cadre formé d'une simple ligne d'épaisseur donnée
  • MatteBorder : cadre formé d'un pavage d'icônes
  • SoftBevelBorder : cadre 3D avec coins arrondis
  • TitledBorder : cadre supportant un titre

Un cadre est créé à l'aide d'un objet de type BorderFactory spécialiés dans la fabrication des cadres :

Border cadre = BorderFactory.createLineBorder(Color.black);
et associé à un composant grâce à la méthode setBorder() :
JButton b = new JButton("ON/OFF");
b.setBorder(cadre);

Composants racines

Ils sont destinés à contenir les composants de l'application ou de l'applet. Ils ne peuvent pas être contenus à l'intérieur d'un autre composant. Ayant besoin d'une fenêtre pour s'afficher, il font appel aux services du système de fenêtrage natif du système d'exploitation : ce ne sont donc pas des composants 100% Java.

Les composants racines de Swing sont listés ci-dessous. Ils sont eux-aussi définis dans le paquet javax.swing mais ils dérivent de la classe AWT java.awt.Window.

  • Fenêtre simple : JWindow
  • Fenêtre avec décorations : JFrame
  • Fenêtre modale : JDialog

Application

Une application qui utilise des composants Swing doit étendre l'une des classes ci-dessus. On supposera qu'il s'agit d'un JFrame, mais ce qui suit s'applique aussi aux deux autres classes.

Un objet de type JFrame est un conteneur particulier qui ne contient qu'un seul composant : une instance de JRootPane ; celle-ci contient un conteneur destiné à contenir les composants de l'interface. Ce conteneur est obtenu en appliquant la méthode :

getContentPane();
sur l'instance de JFrame. Par défaut, son gestionnaire de placement est une instance de BorderLayout.

En conséquence, on doit ajouter les composants de l'application dans l'objet retourné par la méthode ci-dessus et non directement dans l'instance de JFrame. Idem, pour le remplacement du gestionnaire de placement.

Exemple :

import javax.swing.*;
import java.awt.*;

class TestJFrame extends JFrame {
  TestJFrame()
  {
    super("une application");
    Container cp = getContentPane();
    JLabel label = new JLabel("Hello",SwingConstants.CENTER);
    
    cp.add(label, BorderLayout.CENTER);
  }
  
  public static void main(String args[]) {
    JFrame f = new TestJFrame();
    f.setBounds(200,200, 150, 100);
    f.setVisible(true);
  }
}

Gestion du placement

Swing ajoute aux 4 gestionnaires de placement de l'AWT 4 autres gestionnaires de placement : deux sont intégrés à des composants (ScrollPaneLayout et ViewportLayout) et ne sont pas utilisables indépendamment de leur composant respectif, et deux s'utilisent comme ceux de l'AWT : BoxLayout et OverlayLayout, en liaison avec un conteneur.

BoxLayout

Ce gestionnaire de placement permet de ranger des composants soit verticalement (axe des y) soit horizontalement (axe des x). Par exemple, si l'axe de rangement est y, les composants sont placés de haut en bas dans l'ordre auquel ils sont ajoutés. Contrairement au GridLayout, les composants ne sont pas contraints à occuper une même longueur selon l'axe de rangement.

Un BoxLayout est le plus souvent utilisé par l'intermédiaire d'un conteneur de type Box qui possède des méthodes de création de composants invisibles servant à gérer le remplissage de la boîte et l'espacement des composants visibles.

Tailles maximum, minimum et préférée

Il est possible de fixer des tailles minimum, maximum et préférée aux composants avec les méthodes :

  • void setMinimumSize(Dimension)
  • void setMaximumSize(Dimension)
  • void setPreferredSize(Dimension)
et de les lire avec
  • Dimension getMinimumSize()
  • Dimension getMaxnimumSize()
  • Dimension getPreferredSize()

Toutefois, donner des dimensions maximum, minimum et préférée à un composant ne garantit rien sur sa taille réelle, ce sont les gestionnaires de placement qui fixent la taille réelle des composants et rien ne les oblige à respecter ces dimensions.

Aspect visuel d'un composant

Réalisé par un appel à la méthode paint, celle-ci appelle les 3 méthodes suivantes :

  • paintComponent C'est la fonction principale de dessin. Par défaut, elle commence par repeindre le fond si le composant est opaque.
  • paintBorder dessine le cadre s'il y a lieu.
  • paintChildren dessine les composants contenus dans ce composant
dans cet ordre.

Personnaliser l'aspect d'un composant

Pour cela, on doit définir une classe dérivant d'un composant spécialisé (en général JPanel) dans laquelle la méthode paintComponent doit être redéfinie. Il peut être aussi nécessaire de redéfinir la méthode getPreferredSize pour l'adapter au dimension du contenu graphique.

Exemple :

class ImagePanel extends JPanel {
  ImageIcon icon;

  public ImagePanel(String imageName, String description) {
    icon = new ImageIcon(imageName, description);
  }
  public void paintComponent(Graphics g) {
    Insets insets = getInsets();
    super.paintComponent(g);
    icon.paintIcon(this, g, insets.left, insets.top);
  }
  public Dimension getPreferredSize() {
    Insets insets = getInsets();
    return new Dimension(icon.getIconWidth() + insets.left + insets.right, 
			 icon.getIconHeight() + insets.top + insets.bottom);
  }
}