Création de nouvelles fenêtres avec ou sans cadre

publicité
Fenêtre et cadre de fenêtre
Chapitres traités
Fenêtres et cadres
Dans cette étude, nous nous concentrons uniquement sur la mise en oeuvre de nouvelles fenêtres avec ou sans cadres. Nous en
profiterons pour connaître leurs différentes constitutions, mais aussi comment interragir avec le bureau et la barre de tâche.
Fenêtres et cadres
Les fenêtres et les cadres sont les conteneurs de plus niveau des composants Java. JWindow n'est rien d'autre qu'un plein écran graphique d'affichage dans le système de
fenêtrage. Les fenêtres ne comportent pas fioritures : elles conviennent surtout pour faire surgir des écrans et des fenêtres popup. De son côté, JFrame est une classe fille
de JWindow, pourvue d'une bordure et pouvant contenir une barre de menu. Il est également possible de déplacer un cadre sur l'écran et de le redimensionner, à l'aide des
contrôles habituels de l'environnement de fenêtrage.
L'exemple ci-dessous présente un cadre JFrame à gauche et une fenêtre JWindow à droite :
package fenêtres;
import javax.swing.*;
public class Fenêtres {
public static void main(String[] args) {
JFrame cadre = new JFrame("Cadre de fenêtre");
cadre.setSize(300, 250);
cadre.setLocation(150, 100);
JWindow fenêtre = new JWindow();
fenêtre.setSize(300, 250);
fenêtre.setLocation(500, 100);
cadre.setVisible(true);
fenêtre.setVisible(true);
}
}
Décrivons le fonctionnement de ce petit programme :
1. Le constructeur de JFrame peut prendre un argument String indiquant le titre affiché dans la barre de titre. Il est également possible de créer un JFrame sans titre,
puis d'appeler ensuite la méthode setTitle() pour en fournir un ultérieurement.
2. La taille et la position de JFrame et de JWindow sur votre bureau sont déterminées par les méthodes que nous connaissons déjà, c'est-à-dire setSize() et
setLocation().
3. JWindow ne possédant pas de barre de titre, le constructeur de JWindow ne comporte donc pas d'arguments.
4. Une fois que vous avez définis votre JFrame et votre JWindow, nous appelons setVisible(true) pour les afficher à l'écran. La méthode setVisible() revient
immédiatement, sans blocage. Par bonheur, notre application ne se termine pas, même après avoir atteint la fin de la méthode main(), puique les fenêtres sont
encore visibles. Vous pouvez fermer le JFrame en cliquant sur l'icône de fermeture de la barre de titre. Attention, par défaut, JFrame se cache lorsque nous
cliquons dessus, en appelant la méthode setVisible(false), mais ne quitte toujours pas l'application. Heureusement, nous verrons que nous pouvons modifier ce
comportement en appelant la méthode adéquate setDefaultCloseOperation().
5. Il est toutefois impossible de fermer un JWindow dans l'application. Pour mettre fin à votre à l'exécution de l'application Fenêtres, vous devez saisir Crtl-C ou la
touche destinée à tuer un processus sur votre machine. Du coup, JWindow sera plutôt utilisé par un autre JFrame et sera très rarement la fenêtre principale d'une
application.
En Java, une fenêtre de haut niveau, c'est-à-dire une fenêtre qui n'est pas contenue dans une autre fenêtre, est appelée frame (cadre ou fenêtre d'encadrement). Pour
représenter ce niveau supérieur la bibliothèque AWT possède une classe nommée Frame. La version Swing de cette classe est baptisée JFrame, qui étend la classe
Frame et désigne l'un des rares composants Swing qui ne soient pas dessiné sur un canevas (grille). Du coup, les éléments de décoration de la fenêtre (boutons,
barre de titre, icônes, etc.) ne sont pas dessinés par Swing, mais par le système de fenêtrage du système d'exploitation de l'utilisateur.
Les cadres sont des exemples de conteneurs. Cela signifie qu'ils peuvent contenir d'autres composants d'interface tels que les boutons et des champs de texte. De
toute façon, tous les autres composants et conteneurs doivent, à un certain niveau, se trouver dans un JWindow ou un JFrame. Les applets JApplet sont une sorte
de Container. Même les applets doivent être hébergées dans un dans cadre ou une fenêtre, bien qu'en général, nous ne voyons pas son cadre parent car il fait partie
du navigateur qui affiche l'applet.
Mise en place d'un cadre
Je reviendrais plutard sur la fenêtre JWindow. Nous allons maintenant étudier les méthodes employées le plus fréquemment lorsque nous travaillons avec un composant
JFrame. Nous allons procéder par étapes, de l'ossature d'un cadre de fenêtre minimal jusqu'à que ayons une fenêtre relativement sophistiquée.
Création d'un cadre visible où nous pouvons quitter directement l'application
Dans l'exemple précédent, nous avons simplement créé un objet de type JFrame et nous avons utilisé les fonctionnalités présentes dans cette classe. Mais pour que notre
programme présente un intérêt, il va de soi qu'il faut lui associer des fonctionnalités ou des attributs supplémentaires ; de fait, la fenêtre devra pouvoir réagir à certains
événements. Pour cela, il nous faudra généralement définir notre propre classe dérivée de JFrame et créer un objet de ce nouveau type. Par ailleurs, il ne sera plus
nécessaire de préciser à chaque fois le nom de l'objet de type JFrame pour faire appel à la méthode requise.
Voici donc un exemple de codage minimum requis, qui permet de constituer une fenêtre principale d'application placée à l'endroit voulu, avec la possibilité de
quitter l'application quand l'utilisateur le désire.
package cadre;
import javax.swing.JFrame;
public class Fenêtre extends JFrame {
public Fenêtre(String titre) {
super(titre);
setSize(300, 200);
setLocation(100, 100);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new Fenêtre("Première fenêtre") ;
}
}
ou -----------------------------------------------------
public class Fenêtre extends JFrame {
public Fenêtre() {
setTitle("Première fenêtre");
setBounds(100, 100, 300, 200);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new Fenêtre();
}
}
Bien que nous n'ayons rien prévu de particulier, l'utilisateur peut manipuler cette fenêtre comme n'importe qu'elle fenêtre graphique d'un logiciel du commerce, et en
particulier : la retailler, la redimmensionner, la réduire à une icône dans la barre de tâche. Ces fonctionnalités, communes à toutes les fenêtres, sont prises en charge
par la classe JFrame elle-même. Vous n'avez donc pas à vous soucier de la gestion des événements correspondants tels que le clic sur l'une des cases systèmes, le
glissé (drag) d'une bordure ...
Après ce commentaire, analysons notre code source et voyons la liste des méthodes utiles cette classe JFrame :
1. super() : Nous faisons ici explicitement appel au constructeur de la classe JFrame qui accepte un paramètre de type String qui précise tout simplement le titre de
la fenêtre.
2. setTitle() : Au lieu, de faire un appel explicite au constructeur de la classe de base, il est tout à fait possible d'utiliser cette méthode après coup.
3. setSize() : Par défaut, un cadre a une taille assez peu utile de 0 x 0 pixels. Il faut donc utiliser cette méthode pour spécifier la taille requise.
4. setLocation() : cette méthode de la classe Component peut être utilisée pour un JFrame ou un JWindow afin de définir sa position à l'écran. Les coordonnées x et
y sont relatives à l'origine de l'écran (le coin supérieur gauche). Attention, l'axe des abscisses est orientée vers la droite, celui des ordonnées vers le bas (nous
n'avons à pas affaire à un système orthonormé usuel).
5. setBounds() : nous pouvons aussi utiliser une méthode qui positionne et propose les dimensions voulues en une seule fois.
6. setDefaultCloseOperation() : Nous indiquons ensuite ce qui doit se passer lorsque l'utilisateur ferme ce cadre. Dans ce cas précis, le programme doit sortir. Dans
d'autres programmes comprenant éventuellement plusieurs cadres, vous ne souhaiterez pas la sortie du programme si l'utilisateur ferme seulement l'un des
cadres. Par défaut, un cadre est masqué - setVisible(false) - lorsque l'utilisateur le ferme, mais le programme ne se termine pas pour autant. Nous pouvons imposer
un autre comportement lors de la fermeture de la fenêtre, en appelant cette méthode avec l'un des arguments suivants :
DO_NOTHING_ON_CLOSE : ne rien faire. (Intéressant si vous désirez mettre en place une boîte de confirmation de sortie).
DISPOSE_ON_CLOSE : détruire l'objet fenêtre.
EXIT_ON_CLOSE : terminer l'application.
HIDE_ON_CLOSE : cacher la fenêtre (comportement par défaut).
7. setVisible() : Le simple fait de construire un cadre ne l'affiche pas automatiquement. Les cadres sont au départ invisibles. Cela permet au programmeur d'y ajouter
des composants avant l'affichage. Pour afficher le cadre, vous devez impérativement appeler cette méthode.
Positionnement et réglages du cadre de la fenêtre
La classe JFrame ne fournit que peu de méthodes capables de modifier l'aspect d'un cadre. Cependant, grâce à l'héritage, les diverses superclasses de JFrame proposent la
plupart des méthodes permettant d'agir sur la taille et la position d'un cadre ainsi que quelques petits ajouts intéressants.
C'est généralement dans la classe Component (ancêtre de tous les objets d'interface utilisateur graphique) ou dans la classe Window (superclasse du parent de la
classe Frame) que l'on recherche les méthodes permettant de modifier la taille et la position des cadres.
Les plus importantes sont les suivantes :
java.awt.Component
boolean isVisible()
Renvoie true si le composant est visible. Les composants sont initialement visibles par défaut, à l'exception des composants de haut niveau tels que JFrame.
void setVisible(boolean visible)
Affiche ou cache le composant, selon la valeur booléenne proposée (respectivement true ou false).
boolean isShowing()
Vérifie que le composant s'affiche à l'écran. Pour cela, le composant doit être visible et son éventuel conteneur doit être également affiché.
boolean isEnabled()
void setEnabled(boolean activation)
Obtient ou définit l'activité du composant. Un composant activé peut recevoir le focus du clavier. Les composants sont initialement activés.
boolean isOpaque()
void setOpaque(boolean visibilité)
Obtient ou définit une opacité, ou inversement une transparence du composant.
Point getLocation()
Point getLocationOnScreen()
void setLocation(Point p)
void setLocation(int x, int y)
Obtient ou définit la position actuelle du composant par rapport au composant parent. La méthode getLocationOnScreen() précise les coordonnées par
rapport à l'écran.
Dimension getSize()
void setSize(int largeur, int hauteur)
Obtient ou définit la taille actuelle du composant. Un gestionnaire de placement peut modifier la taille d'un composant même après que vous l'avez définie.
Pour changer la taille qu'un composant veut avoir, utilisez alors la méthode setPreferedSize(). D'autres méthodes de Component permettent de définir son
emplacement, mais cette tâche est normalement dévolue à un gestionnaire de placement.
Rectangle getBounds()
void setBounds(Rectangle zonePréférée)
void setBounds(int x, int y, int largeur, int hauteur)
Obtient ou définit à la fois la position et la taille actuelle du composant. Lorsque le composant doit être automatiquement redimensionnné, cette dernière
méthode est systématiquement appelée.
java.awt.Window
void toFront()
Affiche cette fenêtre par-dessus toutes les autres.
void toBack()
Place cette fenêtre au-dessous de la pile des fenêtres du bureau et réorganise les autres fenêtres visibles.
boolean isLocationByPlatform()
void setLocationByPlatform(boolean valider)
Récupère ou défini la propriété locationByPlatform. Lorsque la propriété est définie avant que la fenêtre ne soit affichée, la plate-forme choisit un
emplacement adéquat, légèrement décallée de la dernière qui s'est affichée.
void setLocationRelativeTo(Component référence)
Cette méthode permet de positionner la fenêtre par rapport à un autre composant. Si ce composant n'existe pas ou si vous placez la valeur null, la fenêtre
est alors automatiquement centrée.
java.awt.Frame
void setResizable(boolean visible)
Détermine si l'utilisateur peut modifier la taille du cadre.
void setTitle(String titre)
Affiche le texte de la chaîne passée en argument à la barre de titre du cadre.
void setIconImage(Image icône)
Spécifie l'icône de l'application qui est alors visible sur bord supérieur gauche de la fenêtre ou dans la barre de tâche lorsque la fenêtre est réduite sous
forme d'icône.
boolean isUndecorated()
void setUndecorated(boolean décoration)
Indique si les décorations du cadre sont présentes ou les supprime si le paramètre passé en argument est true.
int getExtendedState()
void setExtendedState(int état)
Récupère ou définit l'état de la fenêtre. Voici les différents états possibles :
-- normal : Frame.NORMAL
-- icônifié : Frame.ICONIFIED
-- maximum en horizontal : Frame.MAXIMIZED_HORIZ
-- maximum en vertical : Frame.MAXIMIZED_VERT
-- plein écran : Frame.MAXIMIZED_BOTH
java.awt.Toolkit
static Toolkit getDefaultToolkit()
Renvoie la boîte à outils par défaut.
Dimension getScreenSize()
Renvoie la taille de l'écran.
Image getImage(String fichierImage)
Image getImage(URL url)
Charge une image à partir du nom du fichier spécifié en argument ou à partir de son URL.
Au vue de ces différentes méthodes, nous pouvons rajouter quelques commentaires supplémentaires :
1. Pour spécifier un placement de fenêtre classique, nous connaissons déjà toutes les méthodes issues de Component comme setSize(), setLocation() et setBounds
().
2. Il est possible de démarrer votre application pour que cette dernière prennent systématiquement tout l'écran. Il est alors judicieux d'utiliser la méthode
setExtendedState(Frame.MAXIMIZED_BOTH).
3. Nous pouvons préférer imposer une taille de fenêtre qui ne bouge plus durant toute sa durée de vie, prenez alors la méthode setResizable(false).
4. Une méthode particulière est certainement setUndecorated(true) qui enlève tout le pourtour du cadre de la fenêtre. Nous nous retrouvons alors exactement comme
dans le cas d'un JWindow.
5. Nous pouvons aussi donner le contrôle du placement au système de fenêtres. Si nous appelons la setLocationByPlatform(true) avant d'afficher le cadre, le
système de fenêtres choisit l'emplacement (mais non la taille), généralement avec un léger décalage par rapport à la dernière fenêtre. Dans la plupart des cas, cette
méthode est plus intéressante que la simple setLocation().
6. Toujours dans le positionnement d'une fenêtre, il est également possible d'utiliser la méthode setLocationRelativeTo() qui par nature positionne le cadre par
rapport à un autre composant. L'intérêt ici est de faire en sorte que la fenêtre s'affiche automatiquement au centre de l'écran en proposant tout simplement la
valeur null à cette méthode.
Pour vous donner une idée de ce que nous pouvons faire avec une fenêtre, nous terminerons ce chapitre en proposant un programme qui positionne le cadre au
centre de l'écran.
Pour connaître la taille de l'écran, nous devons utiliser la classe Toolkit. La classe Toolkit est un dépotoir pour diverses méthodes qui interfacent le système de
fenêtrage natif. La méthode qui nous intéresse ici est la méthode getScreenSize(), qui renvoie la taille de l'écran sous forme d'un objet Dimension (un objet de ce
type stocke simultanément une largeur et une hauteur dans des attributs publics appelés respectivement width et height.
Nous fournissons également une icône à notre cadre. Comme la récupération des images dépend aussi du système, nous employons également la boîte à outils pour
charger une image, à l'aide de la méthode getImage(). Ensuite, nous affectons l'image à l'icône du cadre au travers de la méthode setIconImage().
Les méthodes getScreenSize() et getImage() de la classe Toolkit sont des méthodes non statiques. Vous devez donc créer au préalable un objet représentant la boîte
à outil en utilisant la méthode statique getDefaultToolkit().
package cadre;
import java.awt.*;
import javax.swing.JFrame;
public class Fenêtre extends JFrame {
public Fenêtre() {
setTitle("Cadre centré à l'écran");
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension dimensionEcran = kit.getScreenSize();
int largeur = dimensionEcran.width;
int hauteur = dimensionEcran.height;
setBounds((largeur-300)/2, (hauteur-200)/2, 300, 200);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setIconImage(kit.getImage("icone.gif"));
setResizable(false);
setVisible(true);
}
public static void main(String[] args) {
new Fenêtre() ;
}
}
Comme nous l'avons vu plus haut, de façon plus simple, pour positionner votre cadre au centre de l'écran, il est préférable d'utiliser la méthode
setLocationRelativeTo(null).
Codage correspondant
package cadre;
import java.awt.*;
import javax.swing.JFrame;
public class Fenêtre extends JFrame {
public Fenêtre() {
setTitle("Cadre centré à l'écran");
setLocationRelativeTo(null);
setSize(300, 200);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setIconImage(new ImageIcon("icone.gif").getImage("icone.gif"));
setResizable(false);
setVisible(true);
}
public static void main(String[] args) {
new Fenêtre() ;
}
}
Utilisation de ressources
Restons quelques instants sur ce programme. Nous remarquons que nous avons un problème de déploiement pour l'icône. En effet, cette dernière doit être utilisée à côté
du fichier d'archive, ce qui est assez gênant. L'idéal serait qu'elle soit directement intégrée dans l'archive elle-même. Pour cela, vous devez alors mettre en oeuvre le
mécanisme des ressources. Ainsi, pour obtenir un seul fichier d'archive afin que le déploiement en soit facilité, il est nécessaire que l'icône soit plutôt considérée comme
une ressource.
Utilité des ressources
Il est assez fréquent, à terme, d'avoir besoin de changer le titre d'une fenêtre, de changer un ensemble de messages ou tout simplement de préférer une autre image de fond
de celle prévue initialement. Nous pouvons avoir deux approches pour réaliser ces changements. Soit nous changeons dans le code source les références à ces différents
éléments ce qui nécessite, bien entendu, de tout recompiler. Ou alors nous plaçons ces éléments dans des fichiers séparés afin de proposer les changements à l'extérieur
du programme suivant le désir de l'utilisateur à l'aide d'un tout petit éditeur de texte. L'application se charge ensuite de lire le contenu de ces fichiers afin de configurer
correctement les objets requis. Lorsque vous placez des valeurs à l'extérieur de votre programme, ces valeurs sont considérées comme des ressources.
En reprenant l'exemple de notre application, nous pourrions avoir comme type de ressources, le titre de la fenêtre ainsi que l'icône de l'application :
Quand utiliser une ressource ?
Les classes employées à la fois dans les applets et les applications utilisent généralement des fichiers de données ou de configuration associés tels que :
1. des fichiers d'images et de son ;
2. des fichiers de texte contenant les messages et les libellés des boutons ;
3. des fichiers de données binaires, par exemple pour indiquer la disposition d'une carte ;
4. des fichiers contenant l'ensemble des items des menus afin de prendre en compte la langue du pays.
En Java, un fichier associé de ce type est appelée une ressource.
.
Récupérer les valeurs des ressources
Où devons nous placer ces fichiers ressources ? Bien sûr, il serait pratique de les placer au même endroit que les autres programmes, par exemple dans un fichier
d'archive JAR.
Le chargeur de classe sait comment parcourir chacun des emplacements possibles jusqu'à retrouver le fichier de classes. Toutefois, dans notre cas, nous devons
répéter le processus de recherche manuellement pour localiser les fichiers de ressources associés. La fonctionnalité de chargement de ressource automatise cette
tâche.
Nous suivrons donc les étapes suivantes pour charger une ressource :
1. Charger l'objet Class pour la classe possédant une ressource, par exemple Fenêtre.class ou, si nous somme à l'intérieur de l'objet représentant la dite classe :
this.getClass().
2. Appeler la méthode getRessource() ou la méthode getRessourceAsStream() suivant que vous désirez obtenir le fichier ressource en tant qu'URL ou directement les
valeurs de ce fichier au travers d'un flux adapté. Cette deuxième méthode est souvent préférable.
3. Si la ressource est un fichier image, nous pouvons de nouveau utiliser la méthode getImage() de la boîte à outil Toolkit.
Le chargeur de classes mémorise l'emplacement où il charge la classe ; il peut alors retrouver dans cet emplacement la ressource associée.
.
Par exemple, vous pouvez utiliser les instructions suivantes pour créer l'icône de l'application à partir du fichier image "icône.gif" en suivant cette procédure :
URL url = Fenêtre.class.getRessource("icone.gif");
setIconImage(kit.getImage(url));
Cela revient à rechercher le fichier "icone.gif" au même endroit que celui où vous avez trouvé Fenêtre.class.
Pour lire un fichier texte "titre.txt" représentant le titre de votre application, vous pouvez, par exemple, utiliser les instructions suivantes :
InputStream flux = Fenêtre.class.getRessourceAsStream("titre.txt");
Pour lire ensuite à partir de ce flux, vous devez prendre ensuite un flux de plus niveau afin d'adapter le contenu au type requis. Ici, nous devons récupérer un
texte, il faudra donc prendre un flux capable de retrouver ce texte. Pour une entrée, nous avons besoin de la classe Scanner.
Scanner titre = new Scanner(flux);
this.setTitle(titre.nextLine());
Pour en savoir plus sur les flux, consulter l'étude correspondante : Les flux et les fichiers.
.
Structuration de vos ressources
Il est possible de placer vos fichiers ressources dans un répertoire particulier afin d'éviter de les mélanger avec les fichiers de classes. Vous pouvez même hiérarchiser les
noms des ressources. Par exemple, nous pouvons placer toutes nos ressources dans un répertoire appelé justement <ressources>. La localisation se fera alors de la façon
suivante :
../ressources/titre.txt
et
../resources/icone.gif
Ce nom de ressource relatif est interprété à partir du package de la classe chargeant la ressource. Remarquez l'utilisation obligatoire du séparateur /, quel que soit le
séparateur de répertoire du système d'exploitation sur lequel se trouvent finalement les fichiers de ressources. Ainsi, sous Windows, le chargeur de ressources
convertit automatiquement / en séparateur \.
Le dispositif de chargement de ressources se limite à l'automatisation du chargement des fichiers. Il n'existe pas de méthodes standard pour interpréter le contenu
d'un fichier de ressources. Chaque programme doit interpréter à sa façon le contenu de ses fichiers de ressources.
Codage de l'application en tenant compte de ces ressources
package cadre;
import java.awt.*;
import java.util.Scanner;
import javax.swing.JFrame;
public class Fenêtre extends JFrame {
public Fenêtre() {
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension dimensionEcran = kit.getScreenSize();
int largeur = dimensionEcran.width;
int hauteur = dimensionEcran.height;
setBounds((largeur-300)/2, (hauteur-200)/2, 300, 200);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setIconImage(kit.getImage(getClass().getResource("../ressources/icone.gif")));
Scanner titre = new Scanner(getClass().getResourceAsStream("../ressources/titre.txt"));
setTitle(titre.nextLine());
setResizable(false);
setVisible(true);
}
public static void main(String[] args) {
new Fenêtre() ;
}
}
Nous pouvons proposer une autre approche qui consiste à créer directement des fichiers de configuration. Si le sujet vous intéresse, repportez-vous à l'étude
correspondante : Fichier de configuration.
Structure d'un cadre de fenêtre
La structure de JFrame est étonnement complexe et possède quatre couches superposées comme vous pouvez le constater en observant la figure ci-dessous.
JRootPane
Ce composant est un conteneur qui gère une hiérarchie fixe d'enfants, comprenant toutes
les autres couches nécessaires à la fenêtre. Il est possible d'accéder à ces différents
couches en faisant appel aux méthodes correspondantes comme : getLayeredPane(),
getContentPane(), getGlassPane(). Pour placer un menu, on peut employer la méthode
setJMenuBar(). JRootPane possède également un gestionnaire de disposition spécifique qui
organise automatiquement la disposition des éléments graphiques placés sur la fenêtre.
JLayeredPane
Ce composant fournit les fonctionnalités d'empilement que requiert Swing pour implémenter
les dialogues modaux, les palettes flottantes, les menus contextuels, les bulles d'aide et les
effets graphiques de glisser-déplacer.
ContentPane
La couche de contenu est la partie principale de la fenêtre. C'est sur cette couche que vous
allez placer tous les différents composants graphiques nécessaire à votre application.
GlassPane
Le panneau de verre ( ou la vitre ) doit être soit caché soit transparent ; faute de quoi, il
obscurcit tous les autres composants du JRootPane. Il peut être utilisé pour intercepter les
événements souris destinés aux autres composants du JRootPane, et pour l'affichage de
graphismes temporaires au dessus de ces composants.
La plupart du temps, nous n'avons pas à nous préoccuper de la racine (JRootPane), de la couche supperposée (JLayeredPane) et de la vitre (GlassPane) ; elles sont
nécessaires pour l'organisation de la barre de menus et du contenu, ainsi que pour implémenter l'aspect (look and feel) du cadre. La partie qui intéresse les
programmeurs Swing est plus généralement la couche contenue (ContentPane).
Par exemple, si vous désirez proposer une couleur de fond à votre zone de contenu, la première chose qui vient à l'esprit, c'est de faire appel à la méthode
setBackground() du cadre de la fenêtre. Vous remarquerez que rien ne se passe. Ce qui est normal, puisque c'est uniquement la zone de contenu qui doit avoir
cette couleur là. Il faut donc récupérer d'abord l'objet représentant cette zone particulière au moyen de la méthode getContentPane().
package cadre;
import java.awt.*;
import java.util.Scanner;
import javax.swing.JFrame;
public class Fenêtre extends JFrame {
public Fenêtre() {
setTitle("Structure d'un cadre");
setBounds(100, 100, 300, 200);
setDefaultCloseOperation(EXIT_ON_CLOSE);
getContentPane().setBackground(Color.GREEN);
setVisible(true);
}
public static void main(String[] args) {
new Fenêtre() ;
}
}
Aspect des fenêtres (look and feel)
Nous avons vu en introduction que Swing fournit également une API puissante de look-and-feel modifiable, qui permet de remplacer l'apparence du système d'exploitation
natif au niveau de Java. Swing peut donc facilement changer d'aspect. Toutefois, les divers types de composants possèdent malgré tout des apparences ressemblantes sur
certains points. Ils utilisent notamment la même police et la même grille de couleurs de base. L'ensemble des apparences de composants graphiques s'appellent look-andfeel.
Par défaut, les programmes Swing utilisent l'aspect et le comportement (look and feel) Metal qui est celui proposé par Java et qui offre donc la même apparence
quelque soit le système d'exploitation utilisé. Voici quelques uns des aspects possibles :
1. Metal : javax.swing.plaf.metal.MetalLookAndFeel.
2. CDE/Motif : com.sun.java.swing.plaf.motif.MotifLookAndFeel.
3. Windows : com.sun.java.swing.plaf.windows.WindowsLookAndFeel.
4. Windows Classic : com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel.
Notez que le look and feel Metal est situé dans la paquetage javax.swing. Les autres look and feel se trouvent dans le paquetage com.sun.java et ne doivent pas
nécessairement être présents dans chaque implémentation Java. Actuellement, pour des raisons de copyright, les look and feel Windows et Mac sont uniquement
fournis avec les versions Windows ou Mac de l'environnement d'exécution de Java.
Il existe deux moyens de choisir le look and feel :
1. Le premier consiste à placer, dans les répertoires jre/lib, un fichier swing.properties qui donne à la propriété swing.defaultlaf de la classe le look and feel que vous
désirez employer.
swing.defaultlaf=com.sun.java.swing.plaf.motif.MotifLookAndFeel
Attention, vous devez relancer votre programme pour modifier le look and feel de cette manière. Un programme Swing ne lit qu'une seule fois le fichier
swing.properties, au démarrage.
2. La deuxième façon consiste à modifier dynamiquement le look and feel. Appelez la méthode statique UIManager.setLookAndFeel() et passer-lui le nom du look and
feel souhaité. Appelez ensuite la méthode statique SwingUtilities.updateComponentTreeUI() pour actualiser l'ensemble des composants. Vous n'avez besoin de
fournir qu'un seul composant à cette méthode ; elle trouvera automatiquement les autres.
Pour énumérer toutes les implémentations de look and feel installées, appelez :
UIManager.LookAndFeelInfo[] infos = UIManager.getInstalledLookAndFeels();
String nom = infos[i].getName();
String classe = infos[i].getClassName();
Exemple de codage qui permet de choisir son look and feel et de permettre ainsi la transparence des boutons
package cadre;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Fenêtre extends JFrame implements ActionListener {
private JLabel bienvenue = new JLabel("Bienvenue...");
private JToggleButton changement = new JToggleButton("Dimensions image normale");
private PanneauImage panneauImage = new PanneauImage();
private JPanel panneauSud = new JPanel();
private JPanel panneauCentre = new JPanel();
private
private
private
private
String
String
String
String
metal = "javax.swing.plaf.metal.MetalLookAndFeel";
motif = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
windows = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
windowsClassic = "com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel";
public Fenêtre() throws Exception {
setTitle("Transparence");
setBounds(100, 100, 400, 300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
UIManager.setLookAndFeel(motif);
bienvenue.setFont(new Font("Arial", Font.ITALIC+Font.BOLD, 54));
bienvenue.setForeground(new Color(0, 255, 0, 96));
panneauCentre.setOpaque(false);
panneauCentre.add(bienvenue);
changement.addActionListener(this);
changement.setOpaque(false);
changement.setFocusPainted(false);
changement.setForeground(Color.YELLOW);
panneauSud.setOpaque(false);
panneauSud.add(changement);
panneauImage.setLayout(new BorderLayout());
panneauImage.add(panneauCentre);
panneauImage.add(panneauSud, BorderLayout.SOUTH);
add(panneauImage);
setVisible(true);
}
public static void main(String[] args) throws Exception {
new Fenêtre() ;
}
public void actionPerformed(ActionEvent e) {
if (changement.isSelected()) {
panneauImage.dimensionAutomatique = false;
changement.setText("Taille adaptée à la fenêtre");
}
else {
panneauImage.dimensionAutomatique = true;
changement.setText("Dimensions image normale");
}
panneauImage.repaint();
}
}
class PanneauImage extends JComponent {
boolean dimensionAutomatique = true;
private Image imageFond = new ImageIcon("Cabane dans un champ.jpg").getImage();
@Override
public void paintComponent(Graphics fond) {
if (dimensionAutomatique)
fond.drawImage(imageFond, 0, 0, getWidth(), getHeight(), null);
else
fond.drawImage(imageFond, 0, 0, imageFond.getWidth(null), imageFond.getHeight(null), null);
}
}
Affichage d'image
Pour récupérer un fichier image et ensuite pouvoir l'afficher, nous avons utiliser la classe ImageIcon. En réalité, cette classe construit une icône dont l'image est stockée
dans un fichier. Vous spécifiez le nom de votre fichier en paramètre du constructeur. L'image est alors chargée automatiquement.
Attention, le programme s'interrompt pendant tout le temps de chargement.
.
La classe ImageIcon encapsule une référence à un objet de type Image. C'est cette référence qui nous est utile pour faire du traitement et notamment pour l'afficher
dans la zone correspondante. Cette référence à Image s'obtient à partir de la méthode getImage() de la classe ImageIcon.
Vous avez ci-dessous quelques unes des méthodes intéressantes de cette classe ImageIcon :
javax.swing.ImageIcon
ImageIcon()
ImageIcon(byte[] octets)
ImageIcon(Image image)
ImageIcon(String nomFichierImage)
ImageIcon(URL localisationImage)
Construit une icône respectivement : vierge, à partir d'un tableau d'octets, à partir d'un image déjà existante, à partir d'un fichier ou à partir d'une
localisation quelconque.
int getIconHeight()
Renvoie la hauteur de l'image.
int getIconWidth()
Renvoie la largeur de l'image.
Image getImage()
Récupère l'image proprement dite.
void paintIcon(Component élément, Graphics surface, int x, int y)
Propose des tracés supplémentaires à l'endroit spécifié.
void setImage(Image image)
Propose une nouvelle image à l'icône.
Cette classe est intéressante. Par contre, comme je l'ai déjà évoquée, elle est bloquante au moment du chargement du fichier image. Vous avez aussi également la
possibilité d'utiliser la classe ImageIO qui possède une méthode statique read(). Ici, il sera nécessaire de travailler plutôt avec la classe BufferedImage qui est
concrète et qui hérite de la classe abstraite Image. Pour en savoir plus, reportez-vous à la rubrique suivante : Traitement d'images.
Fenêtre sans cadre
Vous n'êtes pas obligé de prendre systématiquement un cadre de fenêtre. Utilisez, dans ce cas là, la classe JWindow en lieu et place de la classe JFrame. Toutefois, pensez
bien qu'il faut prévoir une sortie à votre programme. Voici, en reprenant l'exemple précédent ce que nous pouvons obtenir :
package cadre;
import
import
import
import
import
import
import
java.awt.*;
java.awt.event.*;
java.awt.image.BufferedImage;
java.io.*;
javax.imageio.ImageIO;
javax.swing.*;
javax.swing.border.*;
public class Fenêtre extends JWindow implements ActionListener {
private JLabel bienvenue = new JLabel("Bienvenue...");
private JToggleButton changement = new JToggleButton("Dimensions image normale");
private PanneauImage panneauImage = new PanneauImage();
private JPanel panneauSud = new JPanel();
private JPanel panneauCentre = new JPanel();
private JButton quitter = new JButton("Quitter");
private
private
private
private
String
String
String
String
metal = "javax.swing.plaf.metal.MetalLookAndFeel";
motif = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
windows = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
classic = "com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel";
public Fenêtre() throws Exception {
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension d = kit.getScreenSize();
setBounds((d.width-400)/2, (d.height-300)/2, 400, 300);
UIManager.setLookAndFeel(motif);
bienvenue.setFont(new Font("Arial", Font.ITALIC+Font.BOLD, 54));
bienvenue.setForeground(new Color(0, 255, 0, 96));
panneauCentre.setOpaque(false);
panneauCentre.add(bienvenue);
changement.addActionListener(this);
changement.setOpaque(false);
changement.setFocusPainted(false);
changement.setForeground(Color.YELLOW);
quitter.addActionListener(this);
quitter.setOpaque(false);
quitter.setForeground(Color.YELLOW);
panneauSud.setOpaque(false);
panneauSud.add(changement);
panneauSud.add(quitter);
panneauImage.setLayout(new BorderLayout());
panneauImage.add(panneauCentre);
panneauImage.add(panneauSud, BorderLayout.SOUTH);
add(panneauImage);
setVisible(true);
}
public static void main(String[] args) throws Exception {
new Fenêtre() ;
}
public void actionPerformed(ActionEvent e) {
if (e.getSource()==quitter) System.exit(0);
if (changement.isSelected()) {
panneauImage.dimensionAutomatique = false;
changement.setText("Taille adaptée à la fenêtre");
}
else {
panneauImage.dimensionAutomatique = true;
changement.setText("Dimensions image normale");
}
panneauImage.repaint();
}
}
class PanneauImage extends JComponent {
boolean dimensionAutomatique = true;
private BufferedImage imageFond;
public PanneauImage() throws IOException {
imageFond = ImageIO.read(new File("Cabane dans un champ.jpg"));
Border incrustée = BorderFactory.createLoweredBevelBorder();
Border cadre = BorderFactory.createLineBorder(new Color(0, 255, 0, 96), 5);
setBorder(BorderFactory.createCompoundBorder(cadre, incrustée));
}
@Override
public void paintComponent(Graphics fond) {
if (dimensionAutomatique)
fond.drawImage(imageFond, 0, 0, getWidth(), getHeight(), null);
else
fond.drawImage(imageFond, 0, 0, imageFond.getWidth(null), imageFond.getHeight(null), null);
}
}
Les méthodes setTitle() et setDefaultCloseOperation() ne sont plus nécessaires. Par contre, vous êtes obligé de prévoir un bouton supplémentaire pour permettre la
sortie du programme. Pour le reste, nous retrouvons exactement le codage normalement prévu pour un JFrame.
Accéder à la barre des tâches
Une application Java peut faire acte de présence dans la barre des tâches du système hôte comme le font certaines applications natives. La classe java.awt.SystemTray
s'occupe de gérer la question. Le terme SystemTray provient de la terminologie KDE et trouve sa correspondance avec la zone de notification de Gnome et la zone d'état de
la barre des tâches de Windows. Tous ces bureaux sont donc supportés par Java. Ensuite, chaque icône représentant le programme à exécuter est gérée par la classe
java.awt.TrayIcon.
La classe SystemTray est un singleton créé au démarrage de la JVM et représente la barre des tâches. Si la barre des tâches n'est pas disponible, la méthode
isSupported() doit vous en informer. Questionner cette méthode est incontournable. Si la méthode renvoie true, vous pouvez alors obtenir l'instance de la barre des
tâches par la méthode getSystemTray(), le point d'accès statique du singleton. SystemTray est un conteneur d'icônes. Ces dernières sont donc encapsulées par
TrayIcon. Il est tout à fait possible d'insérer plusieurs icônes à l'aide de la méthode SystemTray.add() et de les retirer avec la méthode SystemTray.remove().
Une icône se construit, à partir de la classe TrayIcon, avec au moins une image. A cela, nous pouvons ajouter une bulle d'aide et un menu surgissant au moment de
la construction. Nous pouvons aussi introduire ces deux critères supplémentaites plutard, respectivement, au moyen des méthodes setToolTip() et setPopupMenu().
Il est souhaitable que la dimension de l'icône soit adaptée à la barre des tâches, utilisez pour cela la méthode setImageAutoSize(true). Ensuite, le programme
s'exécute normalement lorsque nous double-cliquons sur l'icône. Vous devez donc gérer cet événement en prévoyant un ActionListener. Il est bien sûr tout à fait
possible d'intégrer d'autres types d'événement (le survol de la souris est déjà géré par défaut puisqu'une bulle d'aide apparaît). Précisons pour terminer, que nous
pouvons utiliser la méthode displayMessage() qui permet d'afficher un message sous la forme d'une bulle d'aide avec un titre et un bouton de fermeture
supplémentaires.
Vous avez ci-dessous l'ensemble des méthodes utiles de ces deux classes, avec leurs paramètres respectifs :
java.awt.SystemTray
void add(TrayIcon icône)
Ajoute une nouvelle icône à la barre des tâches.
SystemTray getSystemTray()
Récupère l'instance qui représente la barre des tâches.
TrayIcon[] getTrayIcons()
Récupère, sous forme de tableau, l'ensemble des icônes déjà présentes dans la barre des tâches.
Dimension getTrayIconSize()
Donne la dimension prévue pour chaque icône dans la barre des tâches.
boolean isSupported()
Indique la présence d'une barre de tâches.
void remove(TrayIcon icône)
Enlève une icône à la barre des tâches.
java.awt.TrayIcon
TrayIcon(Image image)
TrayIcon(Image image, String aide)
TrayIcon(Image image, String aide, PopupMenu menuSurgissant)
Construit une icône en précisant son image avec éventuellement une aide contextuelle et un menu surgissant.
void addActionListener(ActionListener écouteur)
Ajoute un écouteur d'événement qui gère l'action double-clic sur l'icône.
void addMouseListener(MouseListener écouteur)
Ajoute un écouteur d'événement qui gère le passage de la souris sur l'icône.
void addMouseMotionListener (MouseMotionListener écouteur)
Ajoute un écouteur d'événement qui gère le déplacement de la souris sur l'icône.
void displayMessage(String titre, String intitulé, TrayIcon.MessageType icône)
Propose une bulle d'aide avec un titre et une icônographie adaptée. Voici d'ailleurs le type de message que vous pouvez mettre en oeuvre :
--- ERROR :
le message correspond à une erreur de manipulation.
--- INFO :
message d'information.
--- NONE : Aucune icône particulière.
message d'alerte.
--- WARNING :
String getActionCommand ()
Renvoie la chaîne de caractères correspondant à la commande lié à l'événement de type ActionEvent.
boolean isImageAutoSize()
Indique si la retaille automatique de l'icône est valide ou pas.
void removeActionListener(ActionListener écouteur)
void removeMouseListener(MouseListener écouteur)
void removeMouseMotionListener(MouseMotionListener écouteur)
Enlève les écouteurs correspondants.
void setActionCommand(String commande)
Spécifie le texte de la commande à exécuter lors du double-clic de la souris sur l'icône.
void setImage(Image image)
Change l'image de l'icône.
void setImageAutoSize(boolean ajusté)
Impose une retaille automatique de l'icône.
void setPopupMenu(PopupMenu popup)
Place un nouveau menu contextuel.
void setToolTip(String message)
Met en place une nouvelle bulle d'aide.
Mise en oeuvre de ces différents composants
Nous allons utiliser nos nouvelles connaissances afin de mettre en oeuvre un programme de conversion entre les €uros et les francs. Cette application sera accessible
depuis une icône sur la notification de la barre des tâches. Voici quelques illustrations qui montrent les différentes phases d'utilisation.
Lorsque vous mettez en oeuvre une icône en barre de tâche, il faut bien comprendre que votre programme doit fonctionner en permanence afin qu'il soit
constamment à l'écoute d'un événement éventuel venant de l'utilisateur. Au minimum, cet événement correspond au double-click sur l'icône elle-même. Lorsque cet
événement a lieu, l'action demandée est simplement d'afficher la fenêtre qui, par ailleurs, est déjà créée.
Attention, pour une fois, lorsque vous cliquez sur le bouton de fermeture de la fenêtre, celle-ci doit juste disparaître sans clôturer le programme, ce qui est justement,
nous l'avons vu, le comportement par défaut d'un JFrame. Du coup, vous n'avez donc plus à utiliser la méthode setDefaultCloseOpération().
Vous avez également la possibilité de prévoir un menu surgissant. De cette manière, il sera possible de quitter définitivement le programme. Comme tout menu
surgissant, vous le faites apparaître avec le clic bouton droit de la souris. Vous devez donc prévoir la gestion des événements des différentes éléments du menu.
Le fait de quitter le programme enlève automatiquement l'icône dans la barre des tâches. Il peut alors être judicieux de prévoir un message d'avertissement en
conséquence :
Codage correspond de cette application
1 package conversion;
2
3 import java.awt.*;
4 import java.awt.event.*;
5 import javax.swing.*;
6
7 public class Conversion extends JFrame implements ActionListener {
8 private JTextField saisie = new JTextField("0");
9 private JButton conversion = new JButton("Conversion");
10 private JLabel résultat = new JLabel("0 Franc");
11 private JPanel panneau = new JPanel();
12 private Image icône;
13 private static Conversion convertisseur;
14 private TrayIcon tray;
15
16 public Conversion() {
17
setTitle("Conversion €uros -> Francs");
18
Toolkit kit = Toolkit.getDefaultToolkit();
19
Dimension dimension = kit.getScreenSize();
20
setBounds(dimension.width-290, dimension.height-140, 280, 80);
21
icône = kit.getImage(getClass().getResource("icone.gif"));
22
setIconImage(icône);
23
panneau.setLayout(new BorderLayout());
24
saisie.setHorizontalAlignment(JTextField.RIGHT);
25
panneau.add(saisie);
26
conversion.addActionListener(this);
27
panneau.add(conversion, BorderLayout.EAST);
28
add(panneau, BorderLayout.NORTH);
29
résultat.setHorizontalAlignment(JLabel.RIGHT);
30
résultat.setBorder(BorderFactory.createEtchedBorder());
31
add(résultat, BorderLayout.SOUTH);
32
getContentPane().setBackground(Color.GREEN);
33
setResizable(false);
34
iconTray();
35 }
36
37 public static void main(String[] args) {
38
convertisseur = new Conversion();
39 }
40
41 public void actionPerformed(ActionEvent e) {
42
final double TAUX = 6.55957;
43
double €uro = Double.parseDouble(saisie.getText());
44
double franc = €uro * TAUX;
45
résultat.setText(franc+" Francs");
46 }
47
48 private void iconTray() {
49
if (SystemTray.isSupported()) {
50
// construction du menu et gestion des événements
51
PopupMenu popup = new PopupMenu();
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
}
83 }
84 }
MenuItem démarrer = new MenuItem("Afficher");
MenuItem quitter = new MenuItem("Quitter");
ActionListener afficher = new ActionListener() {
public void actionPerformed(ActionEvent e) {
convertisseur.setVisible(true);
}
};
ActionListener arrêter = new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
tray.displayMessage("Arrêt de la conversion", "A bientôt...", TrayIcon.MessageType.INFO);
Thread.sleep(4000);
}
catch (InterruptedException ex) { }
finally { System.exit(0);}
}
};
démarrer.addActionListener(afficher);
quitter.addActionListener(arrêter);
popup.add(démarrer);
popup.add(quitter);
// création de l'icône
tray = new TrayIcon(icône, "Conversion entre les €uros et les francs", popup);
tray.setImageAutoSize(true);
tray.addActionListener(afficher);
// placement de l'icône dans la barre de tâche
try {
SystemTray.getSystemTray().add(tray);
}
catch (AWTException ex) {}
Vous remarquez que la mise en oeuvre est très facile. Très peu de méthodes sont utilisées. En réalité, nous passons beaucoup de temps à fabriquer le menu
surgissant avec la gestion d'événement adaptée.
Téléchargement