Au travers de cette étude, nous nous intéressons plus particulièrement à tout ce qui nous permet de faire une sélection : un choix ou un ensemble de choix.
Nous commencerons tout simplement, même si nous l'avons largement utilisé, par un bouton (simple ou double états). Nous en profiterons pour découvrir tous les artifices qu'il est possible de mettre en oeuvre sur ces boutons, afin que l'ergonomie soit la plus intuitive, et donc la plus simple, tout en ayant une apparence des plus sympatiques.
Vous savez maintenant comment recueillir du texte tapé par des utilisateurs. Toutefois, il est souvent préférable de leur offrir un ensemble fini de choix plutôt que de leur demander d'introduire des données dans un composant de texte. Au moyen d'un ensemble de boutons ou d'une liste d'options, vous pouvez indiquer aux utilisateurs les choix mis à leur disposition. De plus, vous n'aurez pas besoin de créer des procédures de contrôle d'erreurs pour ces types de sélections. Dans cette section, vous apprendrez à programmer des cases à cocher, des boutons radio, des listes d'options, des curseurs, etc.

La classe AbstractButton est une classe abstraite et sert de super-classe pour tous les composants Swing dont le comportement est similaire à celui d'un bouton. Comme cette classe est générique, elle définit un grand nombre de propriétés. Les boutons Swing, comme les libellés, peuvent afficher du texte, une image (une icône) ou les deux.
Par défaut, un AbstractButton affiche une seule ligne de texte dans une fonte unique. Cependant, si la propriété du texte commence par "<html> ...", le bouton de texte est formaté en HTML et peut contenir plusieurs fontes et plusieurs lignes.
Les boutons Swing peuvent même afficher des icônes différentes suivant l'état dans lesquels ils se trouvent. Ainsi, en plus de l'icône par défaut, AbstractButton possède des propriétés qui spécifient les icônes à afficher :
Si la propriété rollOver est spécifiée et si la propriété rollOverEnabled est à true (mode par défaut), la rolloverIcon est affichée quand la souris se trouve au dessus du bouton.
Les boutons Swing génèrent trois types d'événements :
Il est possible de gérer finement l'apparence de vos boutons, surtout suivant l'état dans lequel ils se trouvent. Voici ci-dessous quelques unes des propriétés qui s'avèrent intéressantes :
Nous connaissons déjà le principe du modèle MVC. Nous l'avons largement utilisé lors de l'étude précédente. Pour la plupart des composants, la classe modèle implémente une interface dont le nom se termine par Model, d'où l'interface appelée ButtonModel dans le cas des boutons.
La classe abstraite AbstractButton est la classe de base de l'ensemble des composants constituant une sélection ou un choix. Elle possède de nombreuses compétences que nous allons illustrer au travers de notre premier composant concret, celui que nous connaissons déjà bien, JButton.
JButton()
JButton(String libellé)
JButton(Icon icône)
JButton(String libellé, Icon icône)
JButton(Action action)
Il est possible de prérégler un bouton au travers d'une action. Nous avons déjà étudié ce concept lors de l'étude de la gestion des événements. Repportez-vous y pour avoir plus de précision.
Comme nous l'avons découvert dans l'étude des fenêtres, la classe conviviale ImageIcon s'occupe de charger une image pour vous et peut être utilisée pour ajouter une image à un bouton.
Nous allons mettre en oeuvre l'application que nous connaissons bien qui permet de faire une conversion entre les €uros et les francs. Sur le bouton qui permet de réaliser la conversion, je propose cette fois-ci deux icônes :
Vous avez ci-dessous les différents états du bouton de conversion :



Vous remarquez la présence d'un raccourci clavier sur le caractère 'C' du bouton. Le raccourci clavier correspondant est donc <Alt+C>.
.
package boutons; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.text.*; import javax.swing.event.*; public class Conversion extends JFrame { private FormatNombre saisie = new FormatNombre(NumberFormat.getCurrencyInstance()); private FormatNombre résultat = new FormatNombre(new DecimalFormat("#,##0.00 F")); private Bouton validation = new Bouton(); public Conversion() { super("Conversion €uro -> Francs"); setLayout(new FlowLayout()); add(saisie); add(validation); résultat.setEditable(false); add(résultat); saisie.addKeyListener(new KeyAdapter() { @Override public void keyTyped(KeyEvent ev) { validation.setEnabled(true); } }); getContentPane().setBackground(Color.YELLOW); pack(); setResizable(false); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } private class FormatNombre extends JFormattedTextField { public FormatNombre(NumberFormat format) { super(format); setColumns(12); setValue(0); setHorizontalAlignment(RIGHT); setMargin(new Insets(3, 3, 3, 3)); } } private class Bouton extends JButton implements ActionListener, ChangeListener { public Bouton() { super("Convertion", new ImageIcon("validation.gif")); addActionListener(this); addChangeListener(this); setEnabled(false); setFocusPainted(false); setRolloverIcon(new ImageIcon("survol.gif")); setMnemonic('C'); } public void actionPerformed(ActionEvent e) { final double TAUX = 6.55957; double €uro = ((Number)saisie.getValue()).doubleValue(); double franc = €uro * TAUX; résultat.setValue(franc); } public void stateChanged(ChangeEvent e) { setForeground(getModel().isRollover() ? Color.RED : Color.BLACK); } } public static void main(String[] args) { new Conversion(); } }
package boutons; ... public class Conversion extends JFrame { ... private class Bouton extends JButton implements ActionListener, ChangeListener { public Bouton() { super("<html><i>Convertion<br>€uro => Franc</i></html>", new ImageIcon("validation.gif")); ... } ... }
La classe JToggleButton implémente un bouton à bascule : un bouton qui peut être sélectionné ou déselectionné. L'utilisateur peut basculer entre les deux états en cliquant sur le bouton. Comme tous les boutons Swing, un JToggleButton peut afficher du texte et une icône.
JToggleButton()
JToggleButton(Action action)
JToggleButton(String libellé)
JToggleButton(Icon icône)
JToggleButton(String libellé, Icon icône)
JToggleButton(String libellé, boolean sélectionné)
JToggleButton(Icon icône, boolean sélectionné)
JToggleButton(String libellé, Icon icône, boolean sélectionné)
Il est possible de prérégler un bouton au travers d'une action. Nous avons déjà étudié ce concept lors de l'étude de la gestion des événements. Repportez-vous y pour avoir plus de précision.
Par défaut, JToggleButton conserve la trace de son état de sélection avec un objet JToogleButton.ToggleButtonModel. Cette classe JToogleButton.ToggleButtonModel est le bouton ButtonModel utilisé par défaut par les composants JToogleButton, JCheckBox et JRadioButton. Elle surcharge plusieurs méthodes de DefaultButtonModel pour déléguer les informations d'état de sélection du bouton à un objet ButtonGroup (voir plus loin). Les applications n'ont généralement pas besoin d'instancier cette classe.JToggleButton est moins couramment utilisé que ses sous-classes JCheckBox et JRadioButton.
.
Nous aurons souvent besoin d'exploiter un bouton à bascule de deux façons différentes :
Nous allons permettre à l'application précédente de convertir dans les deux sens. Nous rajoutons pour cela un bouton à bascule qui permet d'effectuer le choix désiré.
Vous avez ci-dessous les deux états possibles correspond à la conversion désirée :


package boutons; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.text.*; import javax.swing.event.*; public class Conversion extends JFrame { private FormatNombre € = new FormatNombre(NumberFormat.getCurrencyInstance()); private FormatNombre F = new FormatNombre(new DecimalFormat("#,##0.00 F")); private Bouton validation = new Bouton(); private Bascule choix = new Bascule(); public Conversion() { super("Conversion €uro <-> Francs"); setLayout(new FlowLayout()); add(€); add(validation); add(choix); add(F); getContentPane().setBackground(Color.YELLOW); pack(); setResizable(false); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } private class FormatNombre extends JFormattedTextField { public FormatNombre(NumberFormat format) { super(format); setColumns(12); setValue(0); setHorizontalAlignment(RIGHT); setMargin(new Insets(3, 3, 3, 3)); } } private class Bouton extends JButton implements ActionListener, ChangeListener { public Bouton() { super("Convertion", new ImageIcon("validation.gif")); addActionListener(this); addChangeListener(this); setFocusPainted(false); setRolloverIcon(new ImageIcon("survol.gif")); setMnemonic('C'); } public void actionPerformed(ActionEvent e) { final double TAUX = 6.55957; if (choix.isSelected()) { double €uro = ((Number)€.getValue()).doubleValue(); double franc = €uro * TAUX; F.setValue(franc); } else { double franc = ((Number)F.getValue()).doubleValue(); double euro = franc / TAUX; €.setValue(euro); } } public void stateChanged(ChangeEvent e) { setForeground(getModel().isRollover() ? Color.RED : Color.BLACK); } } private class Bascule extends JToggleButton implements ActionListener { public Bascule() { super("€ => F", new ImageIcon("validation.gif"), true); setHorizontalTextPosition(LEFT); setFocusPainted(false); addActionListener(this); } public void actionPerformed(ActionEvent e) { setText(isSelected() ? " € => F" : "€ <= F"); } } public static void main(String[] args) { new Conversion(); } }
Dans l'exemple ci-dessous, lorsque l'image apparaît sur la zone principale de la fenêtre, nous pouvons, à l'aide d'un bouton à bascule, choisir si elle doit s'afficher en taille réelle ou au contraire si elle doit être totalement visible quelque soit la dimension de la fenêtre.
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 String metal = "javax.swing.plaf.metal.MetalLookAndFeel"; private String motif = "com.sun.java.swing.plaf.motif.MotifLookAndFeel"; private String windows = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel"; private String 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); } }
Il peut être intéressant de regrouper un ensemble de boutons à bascule ou de boutons radio, de telle sorte que lorsque un bouton est sélectionné, les autres se retrouvent automatiquement désactivés. Les boutons s'excluent alors réciproquement. La classe ButtonGroup permet de réaliser cette fonction et impose une exclusion mutuelle (un comportement de bouton radio) à un groupe de boutons. Une fois les boutons ajoutés à un ButtonGroup par la méthode add(), l'exclusion mutuelle est automatique, sans qu'aucune action ultérieure soit nécessaire.
L'objet ButtonGroup est très ordinaire. On pourrait s'attendre à ce qu'il soit un conteneur ou un composant, amis il n'en est rien ; il s'agit simplement d'un objet assistant qui n'autorise le choix que d'un seul bouton à la fois.
La classe ButtonGroup possède une méthode getSelection() qui renvoie une référence au modèle de bouton (ButtonModel), mais pas le bouton lui-même. L'interface ButtonModel possède à son tour la méthode getActionCommand() qui renvoie alors la commande d'action d'un bouton correspondant au libellé du bouton.
Cette fois-ci, prévoyons deux boutons pour choisir la conversion désirée, un pour les Francs (activé par défaut) et l'autre pour les €uros. Lorsqu'un bouton est sélectionné l'autre se désactive automatiquement.
package boutons; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.text.*; import javax.swing.event.*; public class Conversion extends JFrame { private FormatNombre € = new FormatNombre(NumberFormat.getCurrencyInstance()); private FormatNombre F = new FormatNombre(new DecimalFormat("#,##0.00 F")); private Bouton conversion = new Bouton(); private ButtonGroup groupe = new ButtonGroup(); private Bascule choix€ = new Bascule("€"); private Bascule choixF = new Bascule("F"); public Conversion() { super("Conversion €uro <-> Francs"); setLayout(new FlowLayout()); add(€); add(conversion); add(choix€); add(choixF); add(F); choixF.setSelected(true); getContentPane().setBackground(Color.YELLOW); pack(); setResizable(false); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } private class FormatNombre extends JFormattedTextField { public FormatNombre(NumberFormat format) { super(format); setColumns(12); setValue(0); setHorizontalAlignment(RIGHT); setMargin(new Insets(3, 3, 3, 3)); } } private class Bouton extends JButton implements ActionListener, ChangeListener { public Bouton() { super("Convertion", new ImageIcon("validation.gif")); addActionListener(this); addChangeListener(this); setFocusPainted(false); setRolloverIcon(new ImageIcon("survol.gif")); setMnemonic('C'); } public void actionPerformed(ActionEvent e) { final double TAUX = 6.55957; if (choixF.isSelected()) { double €uro = ((Number)€.getValue()).doubleValue(); double franc = €uro * TAUX; F.setValue(franc); } else { double franc = ((Number)F.getValue()).doubleValue(); double euro = franc / TAUX; €.setValue(euro); } } public void stateChanged(ChangeEvent e) { setForeground(getModel().isRollover() ? Color.RED : Color.BLACK); } } private class Bascule extends JToggleButton implements ItemListener { public Bascule(String libellé) { super(libellé); setFocusPainted(false); groupe.add(this); addItemListener(this); } public void itemStateChanged(ItemEvent e) { setForeground(isSelected() ? Color.RED : Color.BLACK); } } public static void main(String[] args) { new Conversion(); } }
Retour sur les événements : La classe abstraite AbstractButton dispose déjà de tous les ingrédients pour gérer correctement tous les types d'événements. Ainsi, les boutons, qui héritent tous de cette classe de base, récupèrent automatiquement tout ce potentiel.
Jusqu'à présent, nous avons surtout utilisé l'événement de type Action. Dans l'exemple qui précéde, et plus généralement lorsque nous avons une gestion globale des boutons à prendre en compte, grâce à la notion de groupe, l'événement de type Item prend alors toute son importance. Je rappelle que cet événement est sollicité à chaque fois qu'un bouton change d'état, qu'il soit sélectionneé directement ou pas. Ainsi, il est possible d'agir comme dans cet exemple, en temps réel, sur la couleur des libellés des boutons.
Un petit peu à l'image des boutons à bascule, si vous souhaitez recueillir une réponse qui se limite à une entrée "oui" ou "non", utilisez plutôt une case à cocher. Ce type de composant, qui est représenté par la classe JCheckBox, s'accompagne d'un intitulé qui permet de les identifier. L'utilisateur clique à l'intérieur de la case pour la cocher, et fait de même pour la désactiver.
Pour ajouter/supprimer la coche, l'utilisateur peut également appuyer sur la barre d'espace si le focus d'entrée se trouve dans la case à cocher.
.
L'action de l'utilisateur sur une case à cocher se limite à la modification de sont état : passage de l'état coché à l'état non coché, ou l'inverse. L'état visuel de la case (cochée, non cochée) est gérée automatiquement par les méthodes de la classe JCheckBox. Lorsque nous cochons une case, son état n'est généralement contrôlé qu'ultérieurement, par exemple au moment où l'utilisateur lance une action. Cela ressemble à un peu à un formulaire : nous pouvons mofifier nos choix tant que nous ne l'avons pas envoyé.
Comme pour les autres boutons, il est également possible de prévoir une icône à la place du libellé ou avec le libellé. Attention, dans ce cas là, l'image (l'icône) représentant la case à cocher n'existe plus.
Les constructeurs suivant vous offrent toutes les opportunités souhaités :
JCheckBox()
JCheckBox(Action action)
JCheckBox(String libellé)
JCheckBox(Icon icône)
JCheckBox(String libellé, Icon icône)
JCheckBox(String libellé, boolean sélectionné)
JCheckBox(Icon icône, boolean sélectionné)
JCheckBox(String libellé, Icon icône, boolean sélectionné)
Il est possible de prérégler un bouton au travers d'une action. Nous avons déjà étudié ce concept lors de l'étude de la gestion des événements. Repportez-vous y pour avoir plus de précision.
Après coup, et indépendamment de l'action de l'utilisateur, nous pouvons, à tout instant, imposer par programme un état donné à l'aide de la méthode setSelected().
Un tel appel de méthode, comme la classe ToogleButton, génère un événement de type Item.
.
De la même façon, nous pouvons connaître à tout instant l'état d'une case à cocher (indépendamment de son éventuel changement d'état provoqué par l'utilisateur) à l'aide de la méthode isSelected().
Comme tout composant, nous ajoutons une case à cocher à un conteneur par la méthode add() de ce dernier.
Retour sur les événements : La classe abstraite AbstractButton dispose déjà de tous les ingrédients pour gérer correctement tous les types d'événements. Ainsi, les boutons, qui héritent tous de cette classe de base, récupèrent automatiquement tout ce potentiel.
Ici aussi, comme pour la classe ToggleButton, nous pouvons aussi bien prendre un événement de type Action qu'un événement de type Item. Par contre, d'après ce que nous avons dit plus haut, beaucoup d'applications ne s'en préoccupent pas, puisque c'est plutôt lors d'une validation par un bouton classique (JButton) et au travers de la méthode isSelected() que l'état des cases à cocher sont réellement identifiées. Ainsi, généralement, la gestion des événements est plutôt différée.
Afin d'illustrer le comportement des cases à cocher, je vous propose de revenir sur une application que nous avons déjà mise en oeuvre. Il s'agit d'une horloge avec un simple affichage numérique. L'affichage de la police de caractères est réglable. Il est possible de demander à avoir les chiffres en italiques et/ou en gras. Dans cette application, dès qu'une case à cocher est sollicité, le changement d'aspect de la police est instantanément répercuté. Pour cela, je prévois une gestion d'événement de type Item.
package horloge; import java.awt.*; import java.awt.event.*; import java.text.DateFormat; import java.util.Date; import javax.swing.*; import javax.swing.border.*; public class Boutons extends JFrame implements ActionListener { private Timer minuteur = new Timer(1000, this); private JLabel heure = new JLabel(); private CaseACocher caseGras = new CaseACocher("Gras"); private CaseACocher caseItalique = new CaseACocher("Italique"); private JPanel panneau = new JPanel(); public Boutons() { super("Horloge"); heure.setFont(new Font("Arial", Font.BOLD+Font.ITALIC, 32)); heure.setHorizontalAlignment(JLabel.CENTER); heure.setBorder(BorderFactory.createLoweredBevelBorder()); // heure.setOpaque(true); // heure.setBackground(Color.YELLOW); add(heure); panneau.add(caseGras); panneau.add(caseItalique); panneau.setBackground(Color.ORANGE); add(panneau, BorderLayout.SOUTH); minuteur.start(); setSize(170, 100); setDefaultCloseOperation(EXIT_ON_CLOSE); setResizable(false); setVisible(true); } public void actionPerformed(ActionEvent e) { heure.setText(DateFormat.getTimeInstance(DateFormat.MEDIUM).format(new Date())); } private class CaseACocher extends JCheckBox implements ItemListener { public CaseACocher(String libellé) { super(libellé, true); setMnemonic(libellé.charAt(0)); addItemListener(this); setOpaque(false); } public void itemStateChanged(ItemEvent e) { int mode = 0; mode += caseGras.isSelected() ? Font.BOLD : 0; mode += caseItalique.isSelected() ? Font.ITALIC : 0; heure.setFont(new Font("Arial", mode, 32)); } } public static void main(String[] args) { new Boutons(); } }
La représentation visuelle des cases à cocher est standard. Nous retrouvons les mêmes apparences quelque soit la technologie utilisée. Il est toutefois possible de personnaliser cet aspect visuel. Comme nous l'avons vu, il suffit de proposer une icône personnelle lorsque la case est non active et une autre lorsque la case est cochée. Vous pouvez en profiter pour en placer une autre qui correspond au passage de la souris au dessus de votre case à cocher.
Cette fois-ci, les libellés n'existent plus (informations redondantes). Deux icônes seulement représentent les deux cases à cocher.
... public class Boutons extends JFrame implements ActionListener { ... private CaseACocher caseGras = new CaseACocher("Gras"); private CaseACocher caseItalique = new CaseACocher("Italique"); private JPanel panneau = new JPanel(); ... private class CaseACocher extends JCheckBox implements ItemListener { public CaseACocher(String libellé) { super(new ImageIcon(libellé+"Inactif.gif"), true); setSelectedIcon(new ImageIcon(libellé+".gif")); Icon icône = new ImageIcon(libellé+"Over.gif"); setRolloverIcon(icône); setRolloverSelectedIcon(icône); setMnemonic(libellé.charAt(0)); addItemListener(this); setOpaque(false); } public void itemStateChanged(ItemEvent e) { int mode = 0; mode += caseGras.isSelected() ? Font.BOLD : 0; mode += caseItalique.isSelected() ? Font.ITALIC : 0; heure.setFont(new Font("Arial", mode, 32)); } } public static void main(String[] args) { new Boutons(); } }
Dans l'exemple précédent, l'utilisateur pouvait activer ou désactiver une ou plusieurs options, ou aucune. De nombreuses situations exigent que l'utilisateur ne puissent choisir qu'une seule option parmi un ensemble de choix. Lorqu'une seconde option est sélectionnée, la première est désactivée.
Cet ensemble d'options est souvent mis en oeuvre au moyen d'un groupe de boutons radio, représentés par la classe JRadioButton. Ils sont ainsi appelés, car ils fonctionnent de la même manière que les boutons d'une radio : lorsque vous appuyez sur un bouton, celui qui était enfoncé ressort.
Notez qu'un groupe de boutons ne contrôle que le comportement des boutons. Si vous voulez grouper les options pour des raisons de présentation, vous devez les placer dans un conteneur tel que JPanel. Comme tout composant, nous ajoutons un bouton radio à un conteneur par la méthode add() de ce dernier.
Comme pour les cases à cocher, les boutons radio s'accompagnent d'un libellé qui indique un choix possible parmi plusieurs autres. Le texte prévu pour le libellé est passé au constructeur. Par défaut, un bouton radio est construit dans l'état non actif. Nous pouvons lui imposer l'état sélectionné en utilisant une autre version de constructeur. Si vous sollicitez plusieurs boutons radio actifs dès le départ, c'est le dernier construit qui est effectivement sélectionné, puisque normalement un seul bouton radio doit être activé.Comme pour les autres boutons, il est également possible de prévoir une icône à la place du libellé ou avec le libellé. Attention, dans ce cas là, l'image (l'icône) représentant la case à cocher n'existe plus.
Les constructeurs suivant vous offrent toutes les opportunités souhaités :
JRadioButton()
JRadioButton(Action action)
JRadioButton(String libellé)
JRadioButton(Icon icône)
JRadioButton(String libellé, Icon icône)
JRadioButton(String libellé, boolean sélectionné)
JRadioButton(Icon icône, boolean sélectionné)
JRadioButton(String libellé, Icon icône, boolean sélectionné)
Les boutons radio n'ont toutefois pas la même apparence que les cases à cocher, ce qui permet de les différencier. Ces dernières se présentent en standard sous la forme d'un carré qui contient une coche une fois qu'elles sont sélectionnées. Les boutons radio sont ronds et contiennent un point lorsqu'ils sont activés.
Après coup, et indépendamment de l'action de l'utilisateur, nous pouvons, à tout instant, imposer par programme un état donné à l'aide de la méthode setSelected().
Un tel appel de méthode, comme la classe ToogleButton, génère un événement de type Item.
.
De la même façon, nous pouvons connaître à tout instant l'état d'un bouton radio (indépendamment de son éventuel changement d'état provoqué par l'utilisateur) à l'aide de la méthode isSelected().
Retour sur les événements : La classe abstraite AbstractButton dispose déjà de tous les ingrédients pour gérer correctement tous les types d'événements. Ainsi, les boutons, qui héritent tous de cette classe de base, récupèrent automatiquement tout ce potentiel.
Ici aussi, comme pour la classe ToggleButton, nous pouvons aussi bien prendre un événement de type Action qu'un événement de type Item. Par contre, d'après ce que nous avons dit plus haut, beaucoup d'applications ne s'en préoccupent pas, puisque c'est plutôt lors d'une validation par un bouton classique (JButton) et au travers de la méthode isSelected() que l'état des boutons radio sont réellement identifiées. Ainsi, généralement, la gestion des événements est plutôt différée.
Afin d'illustrer le comportement des boutons radio, je vous propose de revenir sur l'horloge. Cette fois-ci, il est possible de choisir entre l'heure, la date ou les deux. Dans cette application, dès qu'un bouton radio est sollicité, le changement doit s'opérer instantanément. Pour cela, je prévois une gestion d'événement de type Action. Cette fois-ci, l'événement de type Item n'est pas bon choix. Effectivement seul le bouton sélectionné doit proposer l'action désirée. Ici, le changement d'état d'un bouton n'est pas intéressant, seule la sélection est utile.
package horloge; import java.awt.*; import java.awt.event.*; import java.text.DateFormat; import java.util.Date; import javax.swing.*; import javax.swing.border.*; public class Boutons extends JFrame implements ActionListener { private Timer minuteur = new Timer(1000, this); private JLabel heure = new JLabel(); private ButtonGroup groupe = new ButtonGroup(); private BoutonRadio choixHeure = new BoutonRadio("Heure"); private BoutonRadio choixDate = new BoutonRadio("Date"); private BoutonRadio choixLesDeux = new BoutonRadio("Les deux"); private JPanel panneau = new JPanel(); private DateFormat présentation = DateFormat.getTimeInstance(DateFormat.MEDIUM); public Boutons() { super("Horloge"); heure.setFont(new Font("Arial", Font.BOLD+Font.ITALIC, 32)); heure.setHorizontalAlignment(JLabel.CENTER); heure.setBorder(BorderFactory.createLoweredBevelBorder()); add(heure); choixHeure.setSelected(true); panneau.add(choixHeure); panneau.add(choixDate); panneau.add(choixLesDeux); panneau.setBackground(Color.ORANGE); add(panneau, BorderLayout.SOUTH); minuteur.start(); setSize(380, 100); setDefaultCloseOperation(EXIT_ON_CLOSE); setResizable(false); setVisible(true); } public void actionPerformed(ActionEvent e) { heure.setText(présentation.format(new Date())); } private class BoutonRadio extends JRadioButton implements ActionListener { public BoutonRadio(String libellé) { super(libellé); setMnemonic(libellé.charAt(0)); addActionListener(this); setOpaque(false); groupe.add(this); } public void actionPerformed(ActionEvent e) { if (choixHeure.isSelected()) présentation = DateFormat.getTimeInstance(DateFormat.MEDIUM); else if (choixDate.isSelected()) présentation = DateFormat.getDateInstance(DateFormat.FULL); else if (choixLesDeux.isSelected()) présentation = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM); heure.setText(présentation.format(new Date())); } } public static void main(String[] args) { new Boutons(); } }
En java, JList est un composant qui permet de choisir une ou plusieurs valeurs dans une liste prédéfinie. Comme pour les boutons radio, les listes permettent donc à l'utilisateur d'effectuer un choix parmi plusieurs valeurs. Toutefois, c'est un seul composant qui réalise cette opération. Par ailleurs, elles peuvent être configurées de sorte qu'il soit possible d'effectuer plusieurs choix en une seule fois.
Attention : JList n'hérite pas de AbstractButton.
.
Les éléments de la liste sont généralement des chaînes de caractères. Il est possible toutefois d'utiliser n'importe quel type de composant du moment qu'il hérite justement de la classe Component. Nous pouvons ainsi avoir des éléments de type JLabel, qui permettent donc d'incorporer à la fois du texte et une icône, des éléments qui soient capable d'afficher des images, des éléments qui soient capable d'afficher des dessins, etc. Nous pouvons même envisager d'avoir une liste de chaîne de caractères et d'avoir un rendu (apparence) sous forme de composant spécifique : JLabel, images, dessins, etc.
La sélection d'un élément ou d'un ensemble d'éléments est classique dans un environnement graphique :
Nous pouvons créer des boîtes de liste à l'aide du constructeur adapté à la situation requise :
Il existe trois sortes de boîtes de liste, correspondant chacune à la façon de sélectionner un élément ou un ensemble d'éléments. Il est donc nécessaire, une fois que l'objet JList est construit, de choisir le mode de sélection au travers de la méthode setSelectionMode() en proposant le type approprié délivré par la classe ListSelectionModel :
Par défaut, nous avons affaire à une boîte de liste de type MULTIPLE_INTERVAL_SELECTION.
.
Pour sélectionner une plage de valeur, comme nous l'avons évoqué plus haut, l'utilisateur doit cliquer sur la première, appuyer sur la touche <Maj> et, tout en la maintenant enfoncée, cliquer sur la dernière valeur de la plage. Pour sélectionner plusieurs plages, il doit procéder de même, tout en maintenant en outre la touche <Ctrl> enfoncée.
Par défaut, une boîte de liste affiche toutes les options présentes dans la liste dans la mesure de la capacité du conteneur, ce qui la différencie de la boîte combo qui elle, n'affiche qu'une option à la fois (au repos). Bien entendu, nous pouvons modifier ce comportement initial pour que la visualisation de votre liste corresponde à l'apprence souhaitée.

Initialement, aucune valeur n'est sélectionnée dans la liste. Le cas échéant, nous pouvons forcer la sélection :
Bien entendu, si nous avons une liste de choix, c'est ultérieurement pour récupérer les valeurs sélectionnées afin de réaliser le traitement souhaité :