Penser en Java 2nde édition | - | Sommaire | Préface | Avant-propos | Chapitre : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Annexe : A B C D | Tables des matières | - | Thinking in Java |
On remarquera que showOptionDialog() et showInputDialog() retournent des objets contenant la valeur entrée par l'utilisateur.
Chaque composant capable de contenir un menu, y compris JApplet, JFrame, JDialog et leurs descendants, possède une méthode setJMenuBar() qui prend comme paramètre un JMenuBar (il ne peut y avoir qu'un seul JMenuBar sur un composant donné). On ajoute les JMenus au JMenuBar, et les JMenuItems aux JMenus. On peut attacher un ActionListener à chaque JMenuItem, qui sera lancé quand l'élément de menu est sélectionné.
Contrairement à un système qui utilise des ressources, en Java et Swing il faut assembler à la main tous les menus dans le code source. Voici un exemple très simple de menu :
L'utilisation de l'opérateur modulo [modulus] dans i%3 distribue les éléments de menus parmi les trois JMenus. Chaque JMenuItem doit avoir un ActionListener attaché ; ici le même ActionListener est utilisé partout mais on en aura besoin normalement d'un pour chaque JMenuItem.
JMenuItem hérite d'AbstractButton, et il a donc certains comportements des boutons. En lui-même, il fournit un élément qui peut être placé dans un menu déroulant. Il y a aussi trois types qui héritent de JMenuItem : JMenu pour contenir d'autres JMenuItems (pour réaliser des menus en cascade), JCheckBoxMenuItem, qui fournit un marquage pour indiquer si l'élément de menu est sélectionné ou pas, et JRadioButtonMenuItem, qui contient un bouton radio.
En tant qu'exemple plus sophistiqué, voici à nouveau les parfums de crèmes glacées, utilisés pour créer des menus. Cet exemple montre également des menus en cascade, des mnémoniques clavier, des JCheckBoxMenuItems, et la façon de changer ces menus dynamiquement :
Dans ce programme j'ai placé les éléments de menus dans des tableaux et ensuite j'ai parcouru chaque tableau en appelant add() pour chaque JMenuItem. Ceci rend l'ajout ou la suppression d'un élément de menu un peu moins fastidieux.
Ce programme crée deux JMenuBars pour démontrer que les barres de menu peuvent être échangées dynamiquement à l'exécution du programme. On peut voir qu'un JMenuBar est composé de JMenus, et que chaque JMenu est composé de JMenuItems, JCheckBoxMenuItems, ou même d'autres JMenus (qui produisent des sous-menus). Une fois construit un JMenuBar, il peut être installé dans le programme courant avec la méthode setJMenuBar(). Remarquons que lorsque le bouton est cliqué, il regarde quel menu est installé en appelant getJMenuBar(), et à ce moment il le remplace par l'autre barre de menu.
Lorsqu'on teste "Open", il faut remarquer que l'orthographe et les majuscules/minuscules sont cruciaux, et que Java ne signale pas d'erreur s'il n'y a pas correspondance avec "Open". Ce genre de comparaison de chaînes est une source d'erreurs de programmation.
Le cochage et le décochage des éléments de menus est pris en compte automatiquement. Le code gérant les JCheckBoxMenuItems montre deux façons différentes de déterminer ce qui a été coché : la correspondance des chaînes (qui, comme mentionné ci-dessus, n'est pas une approche très sûre bien qu'on la rencontre) et la correspondance de l'objet cible de l'événement. Il montre aussi que la méthode getState() peut être utilisée pour connaître son état. On peut également changer l'état d'un JCheckBoxMenuItem à l'aide de setState().
Les événements des menus sont un peu inconsistants et peuvent prêter à confusion : les JMenuItems utilisent des ActionListeners, mais les JCheckboxMenuItems utilisent des ItemListeners. Les objets JMenu peuvent aussi supporter des ActionListeners, mais ce n'est généralement pas très utile. En général, on attache des listeners à chaque JMenuItem, JCheckBoxMenuItem, ou JRadioButtonMenuItem, mais l'exemple montre des ItemListeners et des ActionListeners attachés aux divers composants de menus.
Swing supporte les mnémoniques, ou raccourcis clavier, de sorte qu'on peut sélectionner tout ce qui est dérivé de AbstractButton (bouton, menu, élément, et cetera) en utilisant le clavier à la place de la souris. C'est assez simple : pour le JMenuItem on peut utiliser le constructeur surchargé qui prend en deuxième argument l'identificateur de la touche. Cependant, la plupart des AbstractButtons n'ont pas ce constructeur; une manière plus générale de résoudre ce problème est d'utiliser la méthode setMnemonic(). L'exemple ci-dessus ajoute une mnémonique au bouton et à certains des éléments de menus : les indicateurs de raccourcis apparaissent automatiquement sur les composants.
On peut aussi voir l'utilisation de setActionCommand(). Ceci paraît un peu bizarre car dans chaque cas la commande d'action [action command] est exactement la même que le label sur le composant du menu. Pourquoi ne pas utiliser simplement le label plutôt que cette chaîne de remplacement ? Le problème est l'internationalisation. Si on réoriente ce programme vers une autre langue, on désire changer uniquement le label du menu, et pas le code (ce qui introduirait à coup sûr d'autres erreurs). Donc pour faciliter ceci pour les codes qui testent la chaîne associée à un composant de menu, la commande d'action peut être invariante tandis que le label du menu peut changer. Tout le code fonctionne avec la commande d'action, de sorte qu'il n'est pas touché par les modifications des labels des menus. Remarquons que dans ce programme on ne recherche pas des commandes d'actions pour tous les composants de menus, de sorte que ceux qui ne sont pas examinés n'ont pas de commande d'action positionnée.
La plus grosse partie du travail se trouve dans les listeners. BL effectue l'échange des JMenuBars. Dans ML, l'approche du "qui a sonné ?" est utilisée en utilisant la source de l'ActionEvent et en l'émettant vers un JMenuItem, en faisant passer la chaîne de la commande d'action à travers une instruction if en cascade.
Le listener FL est simple, bien qu'il gère les différents parfums du menu parfums. Cette approche est utile si la logique est simple, mais en général, on utilisera l'approche de FooL, BarL et BazL, dans lesquels ils sont chacun attaché à un seul composant de menu de sorte qu'il n'est pas nécessaire d'avoir une logique de détection supplémentaire, et on sait exactement qui a appelé le listener. Même avec la profusion de classes générées de cette façon, le code interne tend à être plus petit et le traitement est plus fiable.
On peut voir que le code d'un menu devient rapidement long et désordonné. C'est un autre cas où l'utilisation d'un GUI builder est la solution appropriée. Un bon outil gérera également la maintenance des menus.
La façon la plus directe d'implémenter un JPopupMenu est de créer une classe interne qui étend MouseAdapter, puis d'ajouter un objet de cette classe interne à chaque composant pour lequel on veut créer le pop-up :
Le même ActionListener est ajouté à chaque JMenuItem de façon à prendre le texte du label du menu et l'insérer dans le JTextField.
Dans un bon outil de GUI, dessiner devrait être assez facile, et ça l'est dans la bibliothèque Swing. Le problème de tout exemple de dessin est que les calculs qui déterminent où vont les éléments sont souvent beaucoup plus compliqués que les appels aux sous-programmes de dessin, et que ces calculs sont souvent mélangés aux appels de dessin, de sorte que l'interface semble plus compliquée qu'elle ne l'est.
Pour simplifier, considérons le problème de la représentation de données sur l'écran. Ici, les données sont fournies par la méthode intrinsèque Math.sin() qui est la fonction mathématique sinus. Pour rendre les choses un peu plus intéressantes, et pour mieux montrer combien il est facile d'utiliser les composants Swing, un curseur sera placé en bas du formulaire pour contrôler dynamiquement le nombre de cycles du sinus affiché. De plus, si on redimensionne la fenêtre, on verra le sinus s'adapter de lui-même à la nouvelle taille de la fenêtre.