Présentation et conteneurs

Chapitres traités   

Au travers de cette étude, nous allons nous consacrer aux composants qui s'occupent plus particulièrement des aspects ergonomiques qui offrent ainsi des renseignements supplémentaires, comme par exemple les bulles d'aide ou les barres de progression.

Nous nous intéresserons également à d'autres composants, qui font suite à l'étude précédente et qui offrent des choix ou des orientations d'évolution dans l'application, qui sont généralement plutôt des conteneurs, comme par exemple les barres d'outil ou les panneaux à onglets.

 

Choix du chapitre Etiquettes et composants d'étiquetage - JLabel

Les étiquettes (ou libellés) sont des composants qui contiennent du texte. Ils ne possèdent pas d'ornements, telle une bordure, et ne réagissent pas aux entrées de l'utilisateur. Vous pouvez employer une étiquette pour identifier des composants comme les champs de saisie, qui, contrairement aux boutons, n'ont pas de libellé.

Pour associer une étiquette à un composant, procédez ainsi :

  1. Construisez un composant JLabel avec le texte voulu.
  2. Placez-le suffisamment près du composant à identifier pour éviter toute ambiguïté.

Création d'une étiquette

Le constructeur d'un objet JLabel permet de spécifier le texte initial ou l'icône et, en option, l'alignement du contenu. Vous pouvez utilisez l'interface du contenu SwingConstants pour spécifier l'alignement grâce aux constantes suivantes qu'elle définit : LEFT, RIGHT, CENTER, TOP et BOTTOM. La classe JLabel est l'une des classes qui implémentent cette interface. Donc pour spécifier l'une de ces constantes, vous pouvez tout aussi bien écrire SwingConstants.RIGHT ou JLabel.RIGHT.

  1. JLabel() : Construit une étiquette vierge, sans texte ni icône.
  2. JLabel(String libellé) : Construit une étiquette avec le texte aligné à gauche.
  3. JLabel(Icon icône) : Construit une étiquette sans texte, avec une icône alignée à gauche.
  4. JLabel(String libellé, int alignementHorizontal) : Construit une étiquette en choisissant l'alignement du texte au moyen d'une des constantes LEFT, CENTER ou RIGHT.
  5. JLabel(Icon icône, int alignementHorizontal) : Construit une étiquette sans texte en choisissant l'alignement de l'icône au moyen d'une des constantes LEFT, CENTER ou RIGHT.
  6. JLabel(String libellé, Icon icône, int alignementHorizontal) : Construit une étiquette avec un texte et une icône placée à gauche du texte en choisissant l'alignement du couple suivant l'une des constantes LEFT, CENTER ou RIGHT.

A tout moment, il est possible de changer les valeurs initiales proposées durant la phase de création. Ainsi, vous avez le loisir :
- de modifier le libellé au moyen de la méthode setText().
- de proposer une autre icône au moyen de la méthode setIcon().
- de changer d'alignement au moyen de la méthode setHorizontalAlignment().

Notez que la prise en compte d'un changement ne nécessite aucun appel supplémentaire tel que repaint(), validate() ou invalidate().
.

Gérer l'apparence pour élaborer des étiquettes personnalisées

Contrairement à la plupart des autres composants, une étiquette n'a ni bordure, ni couleur de fond (le fond est transparent). Ainsi, la méthode setBackground() peut toujours lui être appliquée, mais elle est sans effet. En revanche, nous pouvons toujours agir sur la couleur du texte à l'aide de la méthode setForeground(). Malgré les apparences, nous pouvons toutefois proposer un affichage relativement sophistiqué.


Etiquette de base

Commençons par découvrir le comportement normal d'une étiquette en proposant toutefois un libellé avec son icône. Par ailleurs, nous avons la possibilité de régler l'espacement qui existe entre l'icône et le texte du libellé au moyen de la méthode setIconTextGap(int). J'en profite donc pour prévoir un écartement de 10 pixels entre l'icône à gauche et le libellé à droite :


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

public class Etiquettes extends JFrame {
   private JLabel bienvenue = new JLabel("Bienvenue", new ImageIcon("icone.gif"), JLabel.CENTER);
   
   public Etiquettes() {
      super("Etiquette");
      bienvenue.setFont(new Font("Arial", Font.BOLD+Font.ITALIC, 28));
      bienvenue.setForeground(Color.BLUE);
      bienvenue.setIconTextGap(10);
      add(bienvenue);
      getContentPane().setBackground(Color.WHITE);
      setSize(250, 150);
      setDefaultCloseOperation(EXIT_ON_CLOSE);
      setVisible(true);
   }
   
   public static void main(String[] args) { new Etiquettes(); }
}    
Placement du texte par rapport à l'icône

Nous pouvons modifier le comportement par défaut et ainsi proposer une apparence plus personnalisée comme nous l'avons fait pour l'écartement. Ainsi, comme la méthode setHorizontalAlignment() permet de positionner le libellé dans le sens horizontal, il est également possible de gérer la position verticale du libellé au moyen de la méthode setVerticalAlignment(). Dans ce genre d'idée, nous pouvons changer le placement par défaut du texte par rapport à l'icône au travers, respectivement des méthodes setHorizontalTextPosition() et setVerticalTextPosition() :

...

   public Etiquettes() {
      super("Etiquette");
      bienvenue.setFont(new Font("Arial", Font.BOLD+Font.ITALIC, 28));
      bienvenue.setForeground(Color.BLUE);
      bienvenue.setVerticalAlignment(JLabel.BOTTOM);
      bienvenue.setHorizontalTextPosition(JLabel.CENTER);
      bienvenue.setVerticalTextPosition(JLabel.BOTTOM);
      add(bienvenue);
...
Se servir de l'étiquette comme support d'image

Nous pouvons nous servir du composant JLabel comme support d'image. Attention toutefois, il est nécessaire que la taille de l'image soit préalablement adaptée à la surface que nous désirons prendre en compte. Il faut donc que l'image soit retaillée en dehors de l'application ou alors que le programme lui-même la retaille avant de la proposée à l'étiquette :


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

public class Etiquettes extends JFrame {
   private JLabel bienvenue = new JLabel(new ImageIcon("rouge-gorge.png"));
   
   public Etiquettes() {
      super("Etiquette");
      add(bienvenue);
      getContentPane().setBackground(Color.BLACK);
      setSize(308, 270);
      setDefaultCloseOperation(EXIT_ON_CLOSE);
      setVisible(true);
   }
   
   public static void main(String[] args) { new Etiquettes(); }
}


Spécifier une icône qui indique la non validation du libellé

Il est possible de rendre le libellé inactif. Le texte devient alors grisé quelque soit sa couleur initiale. Nous pouvons encore accentuer cette apparence de non activité en proposant une icône adaptée au travers de la méthode setDisabledIcon().

Un libellé sert souvent pour des champs de texte qui servent à saisir les valeurs désirées par l'utilisateur. Il est possible de faire en sorte que la saisie prennent le focus à partir d'un raccourci clavier. Le problème, c'est que dans la zone de saisie, rien n'apparaît pour indiquer quel raccourci nous avons choisi. La solution consiste à faire apparaitre ce choix au niveau du libellé associé à cette zone de saisie au moyen des méthodes setDisplayedMnemonic(int) et setDisplayedMnemonic(char).

Dans la première méthode, vous spécifiez le code de la touche clavier. Cela s'avère utile dans le cas où vous désirez prendre en compte les touches annexes <Ctrl>, <Alt> et <Shift> alors que dans la seconde méthode, vous spécifiez uniquement le caractère qui vous intéresse.


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

public class Etiquettes extends JFrame implements ActionListener {
   private JLabel bienvenue = new JLabel("Inactif ", new ImageIcon("icone.gif"), JLabel.RIGHT);
   private Timer timer = new Timer(500, this);
   
   public Etiquettes() {
      super("Etiquette");
      bienvenue.setEnabled(false);
      bienvenue.setFont(new Font("Arial", Font.BOLD+Font.ITALIC, 28));
      bienvenue.setForeground(Color.RED);
      bienvenue.setDisabledIcon(new ImageIcon("inactif.gif"));
      bienvenue.setDisplayedMnemonic('a');
      add(bienvenue);
      getContentPane().setBackground(Color.WHITE); 
      timer.start();
      setSize(250, 200);
      setDefaultCloseOperation(EXIT_ON_CLOSE);
      setVisible(true);
   }
   
   public static void main(String[] args) { new Etiquettes(); }

   public void actionPerformed(ActionEvent e) {
      bienvenue.setText(bienvenue.isEnabled() ? "Inactif " : "Actif ");
      bienvenue.setEnabled(!bienvenue.isEnabled());
   }
}
Prise en compte d'une présentation en format HTML

Si la propriété text commence par <html>, l'étiquette est formatée en texte HTML et peut contenir ainsi de multiples fontes sur plusieurs lignes, avec même la possibilité d'incorporer plusieurs images, des tableaux, etc.

Voici ci-dessous un exemple qui permet d'afficher une image avec les dimensions souhaitées, placée avec un encadrement et un libellé au dessous, tout ceci réalisé par un simple tableau HTML.


import javax.swing.*;

public class Etiquettes extends JFrame {
   public Etiquettes() {
      super("Etiquette");
      String html = "<html><table  bgcolor=#FF0000>"
                            +"<tr><td><img src=\"file:///C:/Photos/oiseau.jpg\" width=225 height=180 /></td></tr>"
                            +"<tr><th>Oiseau.jpg</th></tr>"
                            +"</table></html>";
      add(new JLabel(html, JLabel.CENTER));
      setSize(308, 270);
      setDefaultCloseOperation(EXIT_ON_CLOSE);
      setVisible(true);
   }
   
   public static void main(String[] args) { new Etiquettes(); }
}


Mise en oeuvre d'une application qui utilise des JLabel(s)

Pour conclure sur ce sujet, je vous propose de donner la liste des photos présentes dans un répertoire donné sous forme de vignettes. Cette fois-ci, la démarche suivie consiste à retailler systématiquement chacune des photos pour construire des vignettes sous forme d'icônes :

import java.awt.geom.AffineTransform;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;

public class Etiquettes extends JFrame {
   private File[] fichiers;
   private JPanel panneau = new JPanel();
   private final int largeur = 170;
   
   public Etiquettes() throws IOException {
      super("Etiquette");
      fichiers = new File("C:/Photos/").listFiles();
      for (File fichier : fichiers) {
         JLabel étiquette = new JLabel(fichier.getName(), vignette(fichier), JLabel.LEFT);
         étiquette.setHorizontalTextPosition(JLabel.CENTER);
         étiquette.setVerticalTextPosition(JLabel.BOTTOM);
         panneau.add(étiquette);
      }
      add(panneau);
      setSize(550, 190);
      setDefaultCloseOperation(EXIT_ON_CLOSE);
      setVisible(true);
   }
   
   private Icon vignette(File fichier) throws IOException {
      BufferedImage source = ImageIO.read(fichier);
      BufferedImage image = new BufferedImage(largeur, largeur*3/4, source.getType());
      double ratio = largeur / (double)source.getWidth();
      AffineTransform retailler = AffineTransform.getScaleInstance(ratio, ratio);
      int interpolation = AffineTransformOp.TYPE_BICUBIC;
      AffineTransformOp retaillerImage = new AffineTransformOp(retailler, interpolation);
      retaillerImage.filter(source, image);
      return new ImageIcon(image);
   }
   
   public static void main(String[] args) throws IOException { new Etiquettes(); }
}

 

Choix du chapitre Les bulles d'aide - JToolTip

Il est possible, lorsque la souris passe au dessus d'un composant, de faire apparaître un petit message sous le curseur et qui précise le rôle du composant, ce que nous appelons communément, une bulle d'aide. Cette bulle d'aide est assurée par la classe JToolTip. Généralement, toutefois, vous n'avez pas besoin d'implémenter directement cette classe. En effet, tous les composants graphiques, qui héritent donc de JComponent, peuvent accéder à cette fonctionnalité au travers de la méthode setToolTipText(String). Il suffit juste de préciser le texte désiré, et la bulle d'aide s'affichera alors automatiquement à chaque passage du curseur de la souris au dessus de ce composant.

Mise en oeuvre d'une bulle d'aide dans l'application précédente

A titre d'exemple, je vous propose de revoir l'application précédente, et de faire en sorte que lorsque l'utilisateur passe le curseur de la souris au dessus d'une étiquette, nous voyons une bulle d'aide qui apparaît en indiquant les dimensions de l'image originale.

import java.awt.geom.AffineTransform;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;

public class Etiquettes extends JFrame {
   private File[] fichiers;
   private JPanel panneau = new JPanel();
   private final int largeur = 170;
   
   public Etiquettes() throws IOException {
      super("Etiquette");
      fichiers = new File("C:/Photos/").listFiles();
      for (File fichier : fichiers) {
         JLabel étiquette = new JLabel(fichier.getName(), vignette(fichier), JLabel.LEFT);
         étiquette.setHorizontalTextPosition(JLabel.CENTER);
         étiquette.setVerticalTextPosition(JLabel.BOTTOM);
         étiquette.setToolTipText(aide(fichier));
         panneau.add(étiquette);
      }
      add(panneau);
      setSize(550, 190);
      setDefaultCloseOperation(EXIT_ON_CLOSE);
      setVisible(true);
   }
   
   private Icon vignette(File fichier) throws IOException {
      BufferedImage source = ImageIO.read(fichier);
      BufferedImage image = new BufferedImage(largeur, largeur*3/4, source.getType());
      double ratio = largeur / (double)source.getWidth();
      AffineTransform retailler = AffineTransform.getScaleInstance(ratio, ratio);
      int interpolation = AffineTransformOp.TYPE_BICUBIC;
      AffineTransformOp retaillerImage = new AffineTransformOp(retailler, interpolation);
      retaillerImage.filter(source, image);
      return new ImageIcon(image);
   }
   
   private String aide(File fichier) {
      Icon icône = new ImageIcon(fichier.getName());
      return icône.getIconWidth()+" x "+icône.getIconHeight();
   }
   
   public static void main(String[] args) throws IOException { new Etiquettes(); }
}

 

Choix du chapitre Panneau avec barres de défilement - JScrollPane

Dans les études précédentes, nous avons très souvent utilisé le panneau avec barres de défilement. Nous profitons de ce chapitre pour le connaître un peu mieux. JScrollPane est un conteneur pouvant héberger un composant. Autrement dit, un JScrollPane enveloppe un autre composant. Par défaut, si le composant enveloppé est plus grand que le JScrollPane, celui-ci fournit des barres de défilement. Par contre, si le composant interne devient plus petit, ces barres disparaissent instantanément. Ainsi, tout se fait automatiquement suivant la capacité actuelle du composant enveloppé. JScrollPane gère les événements en provenance des barres de défilement et affiche la partie appropriée du composant enveloppé.

Techniquement, JScrollPane est un Container, mais un drôle de conteneur. Il possède en effet son propre gestionnaire de placement, lequel ne peut pas être remplacé. Il ne peut satisfaire qu'un seul composant à la fois. Cela semble très limitatif, mais à tort. Pour placer de nombreux objets dans un JScrollPane, il suffit de placer ces composants dans un JPanel intermédiaire, quel que soit le gestionnaire de placement souhaité, et de placer ensuite ce panneau dans le JScrollPane.

Création d'un panneau avec barres de défilement

Généralement, lorsque vous désirez placer des barres de défilement sur un objet quelconque, il suffit de faire appel au constructeur avec un seul paramètre qui précise quel doit être le composant enveloppé. Dans ce cas là, nous obtenons, suivant le besoin, les barres de défilement horizontale et verticale. Il existe toutefois des situations où nous souhaitons avoir qu'une seule barre active. Nous pouvons alors choisir d'autres constructeurs qui gèrent les différentes situations spécifiques.

Lorsque vous créez un JScrollPane, vous pouvez préciser les conditions d'affichage de ses barres de défilement. Cela s'appelle une politique d'affichage d'une barre de défilement. Il est ainsi possible de différencier la politique d'affichage pour les barres horizontale et verticale. Les constantes ci-dessous, issue de l'interface ScrollPaneConstants, permettent de préciser la politique de chaque barre de défilement :
  1. HORIZONTAL_SCROLLBAR_AS_NEEDED, VERTICAL_SCROLLBAR_AS_NEEDED : n'affiche une barre de défilement que si le composant enveloppé ne tient pas entièrement.
  2. HORIZONTAL_SCROLLBAR_ALWAYS, VERTICAL_SCROLLBAR_ALWAYS : affiche toujours une barre de défilement, quelle que soit la taille du composant enveloppée.
  3. HORIZONTAL_SCROLLBAR_NEVER, VERTICAL_SCROLLBAR_NEVER : n'affiche jamais de barre, même lorsque le composant enveloppé ne tient pas entièrement. Si vous utilisez cette politique pour les deux barres, vous devez proposer un autre moyen de manipuler le JScrollPane.

Par défaut, les politiques sont HORIZONTAL_SCROLLBAR_AS_NEEDED et VERTICAL_SCROLLBAR_AS_NEEDED.
.

  1. JScrollPane() : Crée un panneau sans composant interne avec des barres de défilement qui apparaissent suivant le besoin. Vous devez précisez par la suite le composant qui bénificiera de cette fonctionnalité au travers de la méthode setViewportView(Component).
  2. JScrollPane(Component composantEnveloppé) : Crée un panneau qui intègre le composant enveloppé et qui propose des barres de défilement horizontale et verticale qui apparaissent automatiquement suivant les capacités de ce composant interne et suivant les besoins du moment.
  3. JScrollPane(Component composantEnveloppé, int politiqueVerticale, int politiqueHorizontale) : Crée un panneau qui intègre le composant enveloppé et qui propose éventuellement des barres de défilement horizontale et verticale suivant la politique choisie.
  4. JScrollPane(int politiqueVerticale, int politiqueHorizontale) : Crée un panneau sans composant interne qui propose éventuellement des barres de défilement horizontale et verticale suivant la politique choisie. Vous devez précisez par la suite le composant qui bénificiera de cette fonctionnalité au travers de la méthode setViewportView(Component).

Fonctionnalités et gestion de l'apparence des panneaux avec barres de défilement

Cette classe est donc un conteneur permettant de faire défiler un composant enfant horizontalement et verticalement. Le composant à faire défiler n'est pas un enfant direct du JScrollPane et ne doit donc pas être ajouté directement avec la méthode add(). Il s'agit en fait d'un JViewport (composant qui affiche une partie du composant enfant qu'il contient que nous allons décrire plus loin) contenu à l'intérieur du JScrollPane. Nous spécifions le composant à défiler en le passant au constructeur JScrollPane() ou à la méthode setViewportView(Component).

Tout type de composant peut être utilisé, mais les composants qui implémentent l'interface Scrollable fonctionnent mieux.
.

Voici quelques méthodes utiles qui permettent de gérer l'apparence de ce panneau particulier et de façon plus personnalisé :

  1. setHorizontalScrollBarPolicy(int) et setVerticalScrollBarPolicy(int) : ces méthodes contrôlent la politique d'affichage des ascenceurs. Les valeurs légales sont les constantes ALWAYS, AS_NEEDED et NEVER définies par l'interface ScrollPaneConstants.
  2. setViewportBorder(Border) : cette méthode permet de spécifier un cadre à faire apparaître autour du JViewPort contenant le composant à faire défiler.
  3. setColumnHeaderView(Component) et setRowHeaderView(Component) : En addition du composant principal à faire défiler, JScrollPane supporte les composants d'en-tête de colonne et de rangée. L'en-tête de colonne apparaît au-dessus du composant de défilement principal et défile horizontalement, mais pas verticalement, afin qu'il soit toujours visible au dessus du JScrollPane. De même, le composant d'en-tête de rangée défile verticalement mais pas horizontalement, et est donc toujours visible à la gauche du JScrollPane.
  4. setCorner(String clé, Composant coin) : Le panneau avec barres de défilement peut également afficher des composants quelconques à chacun de ces quatre coins. L'interface ScrollPaneConstants définit des constantes qui spécifient à quel coin un composant doit apparaître : Remarquez que l'espace disponible aux coins d'un JScrollPane est déterminé par la largeur des barres de défilement et par la largeur et la hauteur des en-têtes de colonne et de rangée, le cas échéant.

Gestion des événements associés à la partie visible du panneau avec barres de défilement

Le panneau avec barre de défilement comporte un objet interne représenté par la classe JViewport. Ce composant affiche une partie du composant enfant qu'il contient. Il définit des méthodes servant à faire défiler efficacement le composant enfant à l'intérieur de la zone de visualisation.

Si vous devez gérer les événements associés aux barres de défilement pour avoir un comportement plus personnalisé, vous devez systématiquement passer par cet objet. Ce composant est en effet capable de prendre en compte les événements de type ChangeEvent. Vous devez donc implémenter un écouteur de type ChangeListener et redéfinir la méthode stateChanged(ChangeEvent) prévue par cette interface. La prise en compte de ces événements se fait au travers de la méthode addChangeListener().

Cet objet est également très intéressant pour récupérer les coordonnées ou la zone rectangulaire associées à la partie visible, ou bien pour imposer une nouvelle vue par programme. Voici quelques méthodes qui me paraissent intéressante à connaître :

Quelques méthodes de JViewport
Point getViewPosition()
void setViewPosition(Point coordonnées)
Permet de connaître ou d'imposer la position de la vue dont les coordonnées se situe en haut et à gauche du panneau.
Rectangle getViewRect()
Permet de connaître la zone rectangulaire visible par rapport à l'ensemble du composant enfant.
Dimension getViewSize()
Précise la dimension de la partie visible du composant enfant.

Dans le chapitre suivant, nous implémenterons une application qui prend en compte les événements associés aux barres de défilement du panneau. Nous en profiterons pour utiliser queques unes des méthodes spécifiques à cette classe JViewport.

Mise en oeuvre de certaines fonctionnalités d'un JScrollPane

A titre d'exercice, je vous propose de reprendre l'application précédente en proposant un ascenceur uniquement dans le sens vertical. Grâce à des JLabel(s), les vignettes possèderont en plus des informations supplémentaires sur chacune des photos stockées sur le disque dur. Ainsi, nous précisons le nom du fichier, la dimension de l'image originale, le poids en octets ainsi que la date de stockage.


import java.awt.geom.AffineTransform;
import java.awt.image.*;
import java.io.*;
import java.text.MessageFormat;
import javax.imageio.*;
import javax.swing.*;

public class Etiquettes extends JFrame {
   private File[] fichiers;
   private Box panneau = Box.createVerticalBox();
   private int largeur, hauteur;
   private JScrollPane ascenceur = new JScrollPane(panneau, 
                                 ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, 
                                 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
   private String html = "<html><table>"
                      +"<tr><td>{0}</td></tr>"
                      +"<tr><td>{1} x {2}</td></tr>"
                      +"<tr><td>{3} octets</td></tr>" 
                      +"<tr><td>{4, date, full}</td></tr>" 
                      +"</table></html>"; 
   
   public Etiquettes() throws IOException {
      super("Etiquette");
      fichiers = new File("C:/Photos/").listFiles();
      for (File fichier : fichiers) {
         Icon icône = vignette(fichier);
         String intitulé = MessageFormat.format(html, fichier.getName(), largeur, hauteur, fichier.length(), fichier.lastModified());
         JLabel étiquette = new JLabel(intitulé, icône, JLabel.LEFT);
//         étiquette.setVerticalTextPosition(JLabel.TOP);
         panneau.add(Box.createVerticalStrut(5));
         panneau.add(étiquette);
      }
      JTextField entête = new JTextField("Nombre de photos : "+fichiers.length);
      entête.setHorizontalAlignment(JTextField.CENTER);
      entête.setEditable(false);
      ascenceur.setColumnHeaderView(entête);
      ascenceur.setBorder(BorderFactory.createRaisedBevelBorder());
      add(ascenceur);
      setSize(400, 300);
      setDefaultCloseOperation(EXIT_ON_CLOSE);
      setVisible(true);
   }
   
   private Icon vignette(File fichier) throws IOException {
      BufferedImage source = ImageIO.read(fichier);
      largeur = source.getWidth();
      hauteur = source.getHeight();
      BufferedImage image = new BufferedImage(200, 150, source.getType());
      double ratio = 200 / (double)largeur;
      AffineTransform retailler = AffineTransform.getScaleInstance(ratio, ratio);
      int interpolation = AffineTransformOp.TYPE_BICUBIC;
      AffineTransformOp retaillerImage = new AffineTransformOp(retailler, interpolation);
      retaillerImage.filter(source, image);
      return new ImageIcon(image);
   }
   
   public static void main(String[] args) throws IOException { new Etiquettes(); }
}

 

Choix du chapitre Division d'un panneau - JSplitPane

Un panneau divisé est un conteneur spécial gérant deux composants, chacun dans son propre sous-panneau. Une barre de division ajuste la taille de chaque sous-panneau. Dans un afficheur de document, nous pourrions utiliser un panneau divisé pour afficher la table des matières à côté du document complet.

Un panneau divisé est représenté par la classe JSplitPane. Cette classe est donc un conteneur qui se divise horizontalement ou verticalement pour afficher deux enfants. L'orientation du pavé est spécifié par la propriété orientation, qui doit être l'une des deux constantes HORIZONTAL_SPLIT ou VERTICAL_SPLIT.

Les deux enfants sont spécifiés par une paire de propriétés qui dépendent de l'orientation du JPlitPane :

  1. Si l'orientation est HORIZONTAL_SPLIT, les enfants sont spécifiés respectivement par setLeftComponent() et setRightComponent().
  2. Pour un JSplitPane avec un VERTICAL_SPLIT, les enfants sont spécifiés respectivement par setTopComponent() et setBottomComponent().

La position du diviseur entre les deux pavés d'un JSplitPane peut être donnée par la méthode setDividerLocation(). L'argument peut alors être un entier spécifiant la position en pixels ou un double entre 0.0 et 1.0 qui spécifie un pourcentage de la taille du JSplitPane.

Mode d'utilisation d'un JSplitPane

Le composant JSplitPane permet à l'utilisateur d'ajuster les tailles relatives des deux enfants en faisant glisser le diviseur apparaissant entre les deux enfants. L'ajustement est cependant contraint afin qu'un enfant ne soit jamais plus petit que sa taille minimale. Si nous spécifions à la méthode setContinuousLayout() la valeur true, les enfants sont retaillés pendant que l'utilisateur entraîne le diviseur. Au contraire, si nous spécifions la valeur false à cette méthode, les composants de l'enfant ne change pas de taille jusqu'à ce que l'utilisateur ait finit le déplacement.

Il est possible de régler la taille du diviseur au moyen de la méthode setDividerSize().
.

Création d'un panneau divisé

Nous pouvons spécifier tous les comportement que nous venons de décrire dès la phase de création. Ainsi, nous pouvons choisir l'orientation de la division, incorporer les enfants qui feront partis du panneau divisé, et spécifier enfin si les enfant devront être automatiquement retaillés durant le déplacement du diviseur.

Voici la liste des constructeurs mis à votre disposition :

  1. JSplitPane() : Création d'un panneau divisé suivant l'orientation horizontale sans continuité de redimensionnement des enfants lors du déplacement du diviseur. Vous devrez spécifier les enfants ultérieurement au travers des méthodes setLeftComponent() et setRightComponent().
  2. JSplitPane(int orientation) : Nous retrouvons les mêmes fonctionnalités que précédemment, mais cette fois-ci nous devons choisir l'orientation.
  3. JSplitPane(int orientation, boolean continuité) : En plus de l'orientation, nous précisons la continuité de redimensionnement des enfants.
  4. JSplitPane(int orientation, boolean continuité, Component componentGauche, Component componentDroite) : Par rapport au constructeur précédent, vous spécifiez en plus les enfants droite et gauche (ou haut et bas suivant l'orientation choisie).
  5. JSplitPane(int orientation, Component componentGauche, Component componentDroite) : Même comportement que le constructeur précédent mais sans prendre en compte le retaillage automatique des enfants durant le déplacement du diviseur.

Mise eu oeuvre d'un panneau divisé

Pour valider toutes les fonctionnalités d'un panneau divisé ainsi que la gestion des événements associées à un panneau avec barres de défilement, je vous propose de mettre en oeuvre une application qui permet de visualiser une photo à la fois sous forme de vignette et aussi en grand format.

Sur la partie droite de mon application se trouve la photo en grand format avec des barres de défilement qui permettent de se promener dans la photo lorsque la fenêtre est relativement petite. Sur la partie gauche de l'application, qui est délimité par une division qu'il est possible d'ajuster à sa convenance, se trouve la même photo, mais cette fois-ci sous forme de vignette. Dans cette partie gauche, nous voyons donc la totalité de la photo mais en modèle réduit. Un rectangle rouge sur la vignette nous indique alors la partie visible qui se trouve dans la zone de droite. Nous avons la possibilité de changer la vue à l'aide de la souris en cliquant à l'endroit désiré sur la vignette. La deuxième solution, plus classique, consiste à utiliser les ascenceurs. Le rectangle se déplace alors en conséquence et en temps réel.

Codage correspondant
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class Etiquettes extends JFrame implements ChangeListener {
   private ImageIcon icône = new ImageIcon("oiseau.jpg");
   private double ratio = icône.getIconWidth() / (double)icône.getIconHeight();
   private Image photo = icône.getImage();
   private JLabel grandFormat = new JLabel(icône);
   private JScrollPane ascenceur = new JScrollPane(grandFormat);
   private JViewport vue = ascenceur.getViewport();
   private Zone zone = new Zone();
   private JSplitPane diviseur = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, zone, ascenceur);
   
   public Etiquettes(){
      super("Etiquette");
      diviseur.setDividerLocation(170);
      vue.addChangeListener(this);
      add(diviseur);
      setSize(500, 300);
      setDefaultCloseOperation(EXIT_ON_CLOSE);
      setVisible(true);
   }

   private class Zone extends JComponent {  
      Rectangle rect;
      double proportion;
      
      public Zone() {
         addMouseListener(new Souris());                  
      }
      
      @Override
      protected void paintComponent(Graphics g) {
         g.drawImage(photo, 0, 0, getWidth(),(int) (getWidth() / ratio), null);
         g.setColor(Color.RED);
         g.drawRect(rect.x, rect.y, rect.width, rect.height);
      }

      @Override
      public void setBounds(int x, int y, int width, int height) {
         super.setBounds(x, y, width, height);
         proportion = (double)icône.getIconWidth()/getWidth();
      }
      
      void traceRectangle(Rectangle r) {         
         int x = (int) (r.x / proportion);
         int y = (int) (r.y / proportion);
         int largeur = (int) (r.width / proportion);
         int hauteur = (int) (r.height / proportion);
         rect = new Rectangle(x , y, largeur, hauteur);
         repaint();
      }
      
      private class Souris extends MouseAdapter {
         @Override
         public void mouseClicked(MouseEvent e) {
            int x = (int) ((e.getX() - rect.width / 2) * proportion);
            int y = (int) ((e.getY() - rect.height / 2) * proportion);
            vue.setViewPosition(new Point(x, y));
         }         
      }
   }
   
   public static void main(String[] args) { new Etiquettes(); }

   public void stateChanged(ChangeEvent e) {
     zone.traceRectangle(vue.getViewRect());  
   }
}

 

Choix du chapitre La maîtrise des onglets - JTabbedPane

Il arrive souvent que nous ayons besoin de présenter un ensemble de documents dans une même application. Pour avoir une vue de chacun de ces documents, deux solutions peuvent se présenter. La première consiste à avoir autant de fenêtre fille que de document. Cette approche à connu un grand succès à une certaine époque. Le problème c'est que l'interface visuelle est vite surchargée. Nous préférons maintenant avoir plus de sobriété en plaçant chaque document dans un panneau dédié qui est alors accessible aux travers d'onglets. Cette deuxième approche utilise une seule fenêtre et chaque document peut ainsi être vite consulté.

Un panneau à onglets est représenté par la classe JTabbedPane. Ce composant est un conteneur acceptant un nombre quelconque d'enfants. Il n'affiche qu'un enfant à la fois, mais affiche un onglet pour chacun. L'utilisateur peut alors cliquer sur un onglet pour afficher l'enfant correspondant.

Création d'un panneau à onglets

La mise en oeuvre d'un panneau, avec un certain nombres d'onglets associés à un ensemble de documents quelconques, se fait en deux étapes. Dans un premier temps, vous créez votre panneau vierge de tout onglet, en spécifiant juste éventuellement comment les onglets devront être présentés. Ensuite, une fois que le panneau existe, vous placerez successivement l'ensemble des documents représentés par des onglets libellés à l'aide de la méthode addTab().

Voici donc comment construire un panneau à onglets :

  1. JTabbedPane() : Création d'un panneau à onglets vierge. Les onglets, lorsqu'ils existent, se placent automatiquement en haut du panneau. Si le nombre d'onglets devient important, ces derniers se placent automatiquement sur plusieurs lignes.
  2. JTabbedPane(int placement) : Création d'un panneau à onglets vierge. Les onglets, lorsqu'ils existent, se place dans l'une des quatre orientations prévues suivant la constante spécifiée en argument : JTabbedPane.TOP, JTabbedPane.BOTTOM, JTabbedPane.LEFT ou JTabbedPane.RIGHT. Si le nombre d'onglets devient important, ces derniers se placent automatiquement sur plusieurs lignes.
  3. JTabbedPane(int placement, int règle) : Ce dernier constructeur reprend les fonctionnalités du précédent, mais cette fois-ci vous avez la possibilité de faire en sorte que les onglets, quel que soit leur nombre, reste systématiquement sur une seule ligne. Dans ce cas là, des flèches supplémentaires apparaissent afin que vous puissiez naviguer dans l'ensemble de vos onglets. Voici les deux constantes que vous pouvez alors utiliser : JTabbedPane.WRAP_TAB_LAYOUT (par défaut) ou JTabbedPane.SCROLL_TAB_LAYOUT.

Ajout de nouveaux onglets

Une fois que le panneau est en place, il faut ensuite créer les différents onglets. Bien que nous puissions ajouter un enfant à un JTabbedPane avec les méthodes add() standard, cela n'offre pas beaucoup de souplesse pour spécifier le contenu de son onglet. JTabbedPane offre en fait plusieurs méthodes addTab() qui permettent de spécifier l'enfant avec son libellé, son icône et sa bulle d'aide associée.

  1. void addTab(String titre, Component contenu) : Ajout d'un nouvel onglet avec son titre, en spécifiant le composant enfant qui propose le contenu désiré.
  2. void addTab(String titre, Icon icône, Component contenu) : Par rapport à la méthode précédente, nous rajoutons une icône associée au titre de l'onglet.
  3. void addTab(String titre, Icon icône, Component contenu, String aide) : Cette fois-ci, nous rajoutons également une bulle d'aide qui s'activera lors du passage du curseur de la souris au dessus de l'onglet.
  4. void insertTab(String titre, Icon icône, Component contenu, String aide, int emplacement) : Il est possible également de rajouter un onglet et de l'insérer à un emplacement spécifique. Par rapport au précédentes méthodes, il est alors nécessaire de spécifier la position désirée dans l'ordre des onglets déjà établi.

Navigation et gestion des onglets

L'intérêt des onglets c'est que l'utilisateur dispose d'un moyen simple et ergonomique pour choisir l'élément qu'il désire voir appraître. Il est toutefois possible par programme de demander à visualiser un onglet spécifique. Pour cela, nous employons soit la méthode setSelectedComponent(), soit la méthode setSelectedIndex().

Même si le JTabbedPane ne présente qu'un ensemble de composants à la fois, tous les composants de toutes les pages se trouvent en mémoire en même temps. Si certains de vos composants accaparent le temps CPU ou la mémoire, placez-les dans un état dormant lorsqu'ils sont invisibles. Justement, la méthode setEnabledAt() permet d'activer ou de désactiver un onglet d'après sa position dans la liste.

Par ailleurs, il est possible à tout moment de supprimer les onglets qui ne nous intéressent plus au moyen des méthodes remove() ou removeAll().
.

Par défaut, les onglets apparaissent en haut du JTabbedPane. Nous pouvons cependant surcharger cette valeur par défaut au moyen de la méthode setTabPlacement(). L'argument de cette méthode doit être une constante TOP, BOTTOM, LEFT ou RIGHT.

Par défaut, lorsqu'il y a trop d'onglets sur une même ligne, JTabbedPane les répartit automatiquement sur plusieurs lignes. Ce comportement, donnant l'apparence d'un tiroir à dossiers suspendus, est assez cohérent avec la notion d'onglet, mais il nécessite aussi que lorsque vous sélectionnez un onglet de la rangée du fond, les autres soient ré-agencés afin que celui-ci apparaisse au premier plan. Il est également possible de configurer un panneau à onglet, à l'aide de la méthode setTabLayoutPolicy(), pour qu'il n'utilise qu'une seule rangée d'onglets, avec la règle d'affichage JTabbedPane.SCROLL_TAB_LAYOUT.

Les principales méthodes qui permettent de modifier le comportement de JTabbedPane
  1. void remove(Component contenu) : Suppression de l'onglet et de son contenu correspondant au composant choisi.
  2. void remove(int index) : Suppression de l'onglet spécifié et de son contenu.
  3. void removeAll() : Suppression de tous les onglets et de leur contenu.
  4. void setBackgroundAt(int index, Color fond) : Proposer une couleur de fond pour l'onglet sélectionné.
  5. void setComponentAt(int index, Component contenu) : Proposer un autre contenu pour l'onglet sélectionné.
  6. void setDesabledIconAt(int index, Icon icôneInactive) : Proposer l'icône d'inaction pour l'onglet sélectionné.
  7. void setEnabledAt(int index, boolean actif) : Permet d'activer ou de désactiver un onglet spécifique.
  8. void setForegroundAt(int index, Color fond) : Proposer une couleur du titre pour l'onglet sélectionné.
  9. void setIconAt(int index, Icon icône) : Proposer une nouvelle icône pour l'onglet sélectionné.
  10. void setMnemonicAt(int index, Color fond) : Spécifie un raccourci clavier pour l'onglet sélectionné.
  11. void setSelectedComponent(Component contenu) : Sélectionne l'onglet correspondant au composant choisi.
  12. void setSelectedIndex(int index) : Sélectionne l'onglet.
  13. void setTabLayoutPolicy(int présentation) : Choisi la façon de présenter l'ensemble des onglets, soit sur plusieurs, soit à l'aide d'ascenseurs rajoutés.
  14. void setTabPlacement(int placement) : Position l'ensemble des onglets dans la région souhaitée.
  15. void setTitleAt(int index, String titre) : Spécifie le titre de l'onglet sélectionné.
  16. void setToolTipTextAt(int index, String aide) : Spécifie la bulle d'aide associé à l'onglet sélectionné.

Gestion des événements associés à la sélection d'un onglet

Il est possible de savoir à tout moment si l'utilisateur change d'onglet et de connaître ainsi quel est celui qui a été sélectionné afin de proposer une action sépcifique ou de récupérer tous les renseignements nécessaires.

Comme beaucoup, JTabbedPane est capable de prendre en compte les événements de type ChangeEvent. Vous devez donc implémenter un écouteur de type ChangeListener et redéfinir la méthode stateChanged(ChangeEvent) prévue par cette interface. La prise en compte de ces événements se fait au travers de la méthode addChangeListener().

Pour connaître quel est l'onglet actuellement utilisé, faites appel à la méthode getSelectedIndex(). Ceci dit, il est quelque fois souhaitable de connaitre plus précisément l'élément qui vous intéresse plutôt que l'onglet lui-même au moyen de la méthode getSelectedComponent(). Il existe également un certain nombre de méthodes judicieuses qui peuvent être utiles dans bien des cas :
  1. Color getBackgroundAt(int index) : Donne la couleur de fond de l'onglet choisi.
  2. Rectangle getBoundsAt(int index) : Procure les dimensions et positions de l'onglet choisi.
  3. Component getComponentAt(int index) : Délivre le contenu de l'onglet choisi.
  4. Icon getDisabledIconAt(int index) : Délivre l'icône de l'onglet inactif spécifié.
  5. Color getForegroundAt(int index) : Donne la couleur du titre de l'onglet choisi.
  6. Icon getIconAt(int index) : Délivre l'icône de l'onglet spécifié.
  7. int getMnemonicAt(int index) : Délivre le raccourci clavier de l'onglet spécifié.
  8. Component getSelectedComponent() : Délivre le contenu correspondant à l'onglet actuellement sélectionné.
  9. int getSelectedIndex() : Donne le numéro de l'index correspondant à l'onglet actuellement sélectionné.
  10. int getTabCount() : Retourne le nombre d'onglets présents dans la panneau.
  11. String getTitleAt(int index) : Donne le titre de l'onglet spécifié.
  12. String getToolTipText(MouseEvent événement) : Retourne la bulle d'aide qui se trouve actuellement sous le curseur de la souris.
  13. String getToolTipText(int index) : Retourne la bulle d'aide associée à l'onglet spécifié.
  14. int indexOfComponent(Component contenu) : Retourne la valeur de l'index correpondant au contenu.
  15. int indexOfTab(Icon icône) : Retourne la valeur de l'index correpondant à l'icône.
  16. int indexOfTab(String titre) : Retourne la valeur de l'index correpondant au titre proposé à l'onglet.
  17. boolean isEnabledAt(int index) : Détermine si l'onglet spécifié par son index est actif ou pas.

Mise en oeuvre d'un panneau à onglets

Je vous propose maintenant de mettre en oeuvre une application qui permet de visualiser l'ensemble des photos présentes dans un répertoire particulier. Chaque image disposera de son propre onglet. Ainsi il sera facile de visualiser celle qui nous intéresse. A titre d'exemple et afin de bien comprendre les fonctionnalités des onglets, le premier sera rendu inactif par programme alors que le dernier sera automatiquement sélectionné par défaut. Enfin, les onglets dont les images auront déjà été sélectionnées seront paint en rouge.

Codage correpondant
import java.awt.*;
import java.io.*;
import javax.swing.*;
import javax.swing.event.*;

public class Etiquettes extends JFrame implements ChangeListener {
   private String répertoire = "C:/Photos/";
   private JTabbedPane onglets = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT);
   private JLabel état = new JLabel(" ");
   
   public Etiquettes(){
      super("Photos");
      for (File fichier : new File(répertoire).listFiles()) {
         String nom = fichier.getName();
         Icon icône = new ImageIcon(répertoire+nom);
         String aide = icône.getIconWidth()+" x "+icône.getIconHeight();
         onglets.addTab(nom, new ImageIcon("image.gif"), new JScrollPane(new JLabel(icône)), aide);
      }
      onglets.setSelectedIndex(onglets.getTabCount()-1);
      onglets.setEnabledAt(0, false);
      onglets.setBackground(Color.YELLOW);
      onglets.addChangeListener(this);
      add(onglets);
      add(état, BorderLayout.SOUTH);
      setSize(500, 400);
      setDefaultCloseOperation(EXIT_ON_CLOSE);
      setVisible(true);
   }

   public static void main(String[] args) { new Etiquettes(); }

   public void stateChanged(ChangeEvent e) {   
      int index = onglets.getSelectedIndex();
      onglets.setBackgroundAt(index, Color.RED);
      String titre = onglets.getTitleAt(index);
      Icon icône = onglets.getIconAt(index);
      String aide = onglets.getToolTipTextAt(index);
      état.setText(titre+" : "+aide);
      état.setIcon(icône);
   }
}

 

Choix du chapitre Barre de progression - JProgressBar

La classe JProgressBar implémente une barre de progression : un composant qui affiche graphiquement une valeur entière non ajustable. Elle est généralement utilisée pour afficher la progression d'un programme pendant une tâche consomatrice en temps, mais peut également simuler l'affichage d'un équaliseur graphique.

ProgressMonitor est une classe utile qui affiche une JProgressBar dans une boîte de dialogue. Comme JScrollBar et JSlider, JProgressBar utilise un BoundedRangeModel pour maintenir son état.

Propriétés utiles

  1. La propiété value est la plus importante. Elle spécifie la valeur actuellement affichée. Ainsi, pour mettre à jour la progression, vous passez, bien entendu, par la méthode setValue(). JProgressBar lance un javax.swing.event.ChangeEvent quand sa propriété value change.
  2. Les propriétés minimum et maximum indiquent l'intervalle possible des valeurs. La propriété value doit donc se trouver entre les valeurs minimum et maximum.
  3. La propriété orientation doit être l'une des constantes HORIZONTAL ou VERTICAL définies par la classe SwingConstants.
  4. Nous pouvons également placer la propriété borderPainted à false si vous ne désirez plus avoir un cadre entourant la progression.
  5. Enfin, vous pouvez activer la propriété stringPainted si vous désirez visualiser le pourcentage de progression à l'intérieur de la barre.
  6. Il est d'ailleurs possible de spécifier un autre texte que le poucentage de progression au moyen de la propriété string.

Exemple qui permet de transférer de gros fichiers sur le réseau

Lorsqu'un utilisateur émet une commande pour laquelle le traitement est long, vous devez impérativement mettre en place un thread séparé pour qu'il effectue le travail afin que l'IHM ne soit pas bloqué. Il existe une classe spécifique SwingWorker qui permet de mettre en place un thread en tâche de fond, qui de temps en temps, permet de s'occuper les composants Swing afin de permettre ainsi de réaliser le suivi d'une progression. Grâce à cette classe, l'utilisateur garde toujours la main sur son IHM.

Pour en savoir plus sur cette classe SwingWorker et sur les threads en général, reportez-vous sur l'étude correspondante.
§

Afin de bien maîtriser le concept de thread avec les composants Swing, je vous propose de mettre en oeuvre un système client-serveur qui permet de transférer des gros fichiers sur le réseau et de contrôler ainsi la progression du transfert.

Deux programmes doivent donc être mise en place, d'une part le service qui stocke (uniquement, c'est un cas d'école) le fichier désiré dans un répertoire prédéfini, et d'autre part le client qui permet de choisir le fichier et de l'envoyer ensuite au service, sur le poste distant. Voici, la représentation de la partie cliente.

Vous pourrez rajouter par la suite des commandes supplémentaires, à la fois sur le client et le serveur, afin de pouvoir récupérer les fichiers stockés, de supprimer les fichiers déjà enregistrés, de lister les fichiers déjà présents, etc.

Codage correspondant côté client
package réseau;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.*;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.swing.*;

public class Client extends JFrame {
   private JFileChooser sélection = new JFileChooser();
   private JToolBar boutons = new JToolBar();
   private JTextField résultat = new JTextField("Stockage de fichiers dans le serveur distant - Faites votre choix            ");
   private JProgressBar progression = new JProgressBar();
   private final int BUFFER = 4096;

   public Client() {
      super("Archivage de fichiers");
      add(boutons, BorderLayout.NORTH);
      boutons.add(new AbstractAction("Archiver") {
         public void actionPerformed(ActionEvent e) {
            if (sélection.showSaveDialog(null) == JFileChooser.APPROVE_OPTION)
                new TransfertFichier(sélection.getSelectedFile()).execute();
         }
      });
      boutons.addSeparator();
      boutons.add(progression);
      boutons.addSeparator();
      progression.setStringPainted(true);
      progression.setVisible(false);
      résultat.setEditable(false);
      résultat.setMargin(new Insets(3, 3, 3, 3));
      add(résultat, BorderLayout.SOUTH);
      pack();
      setLocationRelativeTo(null);
      setDefaultCloseOperation(EXIT_ON_CLOSE);
      setVisible(true);
   }

   public static void main(String[] args) { new Client(); }
   
   private class TransfertFichier extends SwingWorker<String, Integer> {
      private File fichier;
      private int octetsLus;

      public TransfertFichier(File fichier) {
         this.fichier = fichier;
      }
      
      @Override
      protected String doInBackground() throws Exception {
         try {
            résultat.setText("Le fichier ("+fichier.getName()+") est en cours de transfert");
            progression.setVisible(true);
            progression.setValue(0);
            BufferedInputStream lecture = new BufferedInputStream(new FileInputStream(fichier));
            progression.setMaximum(lecture.available());
            Socket service = new Socket("localhost", 5588);
            ObjectOutputStream envoi = new ObjectOutputStream(new BufferedOutputStream(service.getOutputStream()));
            envoi.writeObject(fichier.getName());
            envoi.writeInt(lecture.available());
            envoi.writeInt(BUFFER);
            byte[] octets = new byte[BUFFER];
            while (lecture.available() > 0) {
               if (lecture.available() < BUFFER) octets = new byte[lecture.available()];
               lecture.read(octets);
               envoi.write(octets);
               envoi.flush();
               publish(octetsLus += BUFFER);
            }
            lecture.close();
            service.close();
            return "Le fichier ("+fichier.getName()+") est entièrement transféré";
         }
         catch (FileNotFoundException ex) {
            return "ATTENTION : Le fichier n'existe pas";
         }
         catch (IOException ex) {
            return "ATTENTION : Impossible d'envoyer le fichier";
         }
      }

      @Override
      protected void process(List<Integer> nombres) {
         progression.setValue(nombres.get(nombres.size()-1));
      }
      
      @Override
      protected void done() {
         try {
            résultat.setText(get());
            progression.setVisible(false);
         }
         catch (Exception ex) { }
      }     
   }
}
Codage correspondant côté serveur
package réseau;

import java.io.*;
import java.net.*;

public class Serveur  {
   private static String stockage = "G:/Stockage/";
   
   public static void main(String[] args) throws IOException, ClassNotFoundException {
      ServerSocket service = new ServerSocket(5588);
      while (true) {
         Socket client = service.accept();
         try {
            ObjectInputStream réception = new ObjectInputStream(new BufferedInputStream(client.getInputStream()));
            String nom = (String) réception.readObject();
            int taille = réception.readInt();
            int buffer = réception.readInt();
            int nombre = taille / buffer;
            byte[] octets = new byte[buffer];
            byte[] reste = new byte[taille % buffer];
            BufferedOutputStream fichier = new BufferedOutputStream(new FileOutputStream(stockage + nom));
            for (int i = 0; i < nombre; i++) {
               réception.readFully(octets);
               fichier.write(octets);
            }
            réception.readFully(reste);
            fichier.write(reste);
            fichier.close();
            client.close();
         } 
         catch (Exception ex) {}         
      }  
   }
}

 

Choix du chapitre Les barres d'outils - JToolBar

Une barre d'outil est une barre contenant des boutons qui permettent d'accéder rapidement aux commandes les plus fréquemment utilisées dans un programme. La particularité des barres d'outils est de pouvoir être déplacées n'importe où. Vous pouvez ainsi la faire glisser vers l'un des quatre bords du cadre. Lorsque vous relâchez le bouton de la souris, la barre d'outils est placée au nouvel emplacement.

La barre d'outil peut même être complètement détachée du cadre. Elle est alors contenue dans son propre cadre. Lorsque vous fermez le cadre contenant une barre d'outils détachée, celle-ci revient dans le cardre d'origine.

Création d'une barre d'outils

Les barres d'outils sont très facile à construire. Par défaut, elles sont positionnées horizontalement, mais il est possible de choisir la position verticale. Vous pouvez aussi spécifier un titre au moment de la création, il apparaîtra lorsque la barre d'outils sera détachée.

  1. JToolBar() : Création d'une barre d'outils en position horizontale.
  2. JToolBar(int orientation) : Création d'une barre d'outils suivant l'orientation choisie JToolBar.HORIZONTAL ou JToolBar.VERTICAL (éventuellement SwingConstants.HORIZONTAL ou SwingConstants.VERTICAL).
  3. JToolBar(String nom) : Création d'une barre d'outils avec un nom qui apparaît lorsque la barre est détachée.
  4. JToolBar(String nom, int orientation) : Création d'une barre d'outils avec un nom et son orientation.

Ajout de composants à la barre d'outils

Les boutons sont les composants les plus courants des barres d'outils. Il n'y a toutefois aucune restriction en ce qui concerne les composants pouvant être ajoutés. Vous pouvez, par exemple, ajouter une zone de liste déroulante à une barre d'outils. Pour ajouter un composant à une barre d'outil, il suffit de faire appel à la méthode add() classique.

Une barre d'outils peut contenir des composants de séparation spéciaux qui servent à regrouper les outils liés fonctionnellement entre eux et à les séparer des autres. Nous pouvons ajouter un séparateur à un JToolBar avec la méthode addSeparator().

  1. void add(Component contenu) : Ajout d'un composant quelconque à la droite des autres composants déjà placés.
  2. void add(Action action) : La classe JToolBar possède aussi une méthode spéciale pour ajouter un objet de type Action au lieu d'un composant. Un JButton approprié est alors automatiquement créé, qui suit l'état activé de l'action pour que si l'action est désactivée, le bouton le soit aussi. La classe JMenu a la même possibilité d'accepter des objets Action pour les enfants. L'intérêt c'est que les actions fréquemment utilisées apparaissent souvent à la fois dans les composants JMenu et JToolBar, mais ces actions sont décrites une seule fois pour ces deux types de conteneurs. Ce sujet sera traité durant l'étude des menus.
  3. void addSeparator() : Ajoute un séparateur à l'emplacement actuel qui permet de regrouper les composants suivant leurs fonctionnalités.
  4. void addSeparator(Dimension taille) : Nous pouvons également proposer un séparateur avec une taille personnalisée.

Fonctionnalités de la barre d'outils

Pour ajouter votre barre d'outils à l'application, pensez, comme d'habitude, a prévoir les contraintes de placement dans la méthode add() de la fenêtre de la façon suivante :

add(barre, BorderLayout.NORTH);

Nous pouvons interdire à une barre d'outils de flotter en utilisant ainsi la méthode setFloatable(false). La barre reste alors dans la position où nous l'avons placée. Nous pouvons également régler les marges internes au moyen de la méthode setMargin(Insets).

Mise en oeuvre d'une barre d'outils

Nous allons mettre en oeuvre une barre d'outils qui comporte une zone de saisie, une boîte combo et un bouton. Cette barre d'outils s'intègre dans une application qui permet de faire une conversion entre les €uros et les francs. Dans la partie centrale de cette application, nous avons un historique de tous les calculs qui sont réalisés au travers de la barre d'outils.

Codage correspondant
import java.awt.*;
import java.awt.event.*;
import java.text.*;
import javax.swing.*;

public class Conversion extends JFrame implements ActionListener {
   private JToolBar outils = new JToolBar("Conversions");
   private JTextArea historique = new JTextArea();
   private JFormattedTextField saisie = new JFormattedTextField(0.0);
   private JComboBox choix = new JComboBox(new String[] {"€uro => Franc", "Franc => €uro"});
   private JButton conversion = new JButton("Conversion");
   private MessageFormat ligne = new MessageFormat("({0}) {1, number, currency}  = {2, number, #,##0.00 F}");
   
   public Conversion(){
      super("Conversion €uros <--> Francs");
      saisie.setColumns(10);
      saisie.setBackground(Color.YELLOW);
      conversion.addActionListener(this);
      historique.setMargin(new Insets(5, 3, 5, 3));
      historique.setBackground(Color.ORANGE);
      outils.add(saisie);
      outils.add(choix);
      outils.addSeparator();
      outils.add(conversion);      
      add(outils, BorderLayout.NORTH);
      add(new JScrollPane(historique));
      setSize(400, 300);
      setDefaultCloseOperation(EXIT_ON_CLOSE);
      setVisible(true);
   }

   public static void main(String[] args) { new Conversion(); }

   public void actionPerformed(ActionEvent e) {
      final double TAUX = 6.55957;
      double €uro, franc;
      if (choix.getSelectedIndex()==0) {
         €uro = (Double)saisie.getValue();
         franc = €uro * TAUX;
      }
      else {
         franc = (Double)saisie.getValue();
         €uro = franc / TAUX;
      }
      historique.append(ligne.format(new Object[] {choix.getSelectedItem(), €uro, franc})+'\n');
   }
}