Création interface graphique avec Swing : les bases


précédentsommairesuivant

7. Proposer une liste de choix à l'utilisateur

Après avoir vu les champs de texte, on va maintenant voir un nouveau champ de saisie, les listes déroulantes. En Swing, il existe 2 sortes de listes déroulantes :

  • JList, liste déroulante assez avancée qui permet d'afficher plusieurs éléments à la fois.
  • JComboBox, c'est une liste déroulante normale, de celle que vous voyez partout dans les applications.

On va se contenter d'utiliser la JComboBox dans cet article, la JList sera peut-être pour plus tard dans un autre article.

7.1. Deux manières d'utiliser JComboBox

Il existe 2 manières de manipuler des JComboBox, soit on utilise directement les méthodes de manipulations des éléments de la JComboBox soit on développe notre propre modèle de liste.

On va maintenant détailler ces deux méthodes.

7.1.1. Méthodes de JComboBox

La première manière de faire, c'est simplement d'utiliser les méthodes que nous propose JComboBox :

  • addItem(Objet item), cette méthode va ajouter un nouvel objet à la liste
  • removeItem(Objet item), cette méthode va enlever l'objet de la liste
  • removeAllItems(), cette méthode va vider la liste déroulante
  • getSelectedItem(), cette méthode retourne l'objet qui est actuellement sélectionné

Comme vous le voyez, c'est très simple.

On va maintenant utiliser ces méthodes. Pour cela, on va imaginer une fenêtre avec plusieurs boutons et 2 listes déroulantes. La première liste déroulante contiendra une série d'éléments qu'on pourra copier dans la deuxième liste avec un bouton et un deuxième bouton permettra d'enlever des éléments de la deuxième liste. On va également ajouter un troisième bouton qui permettra de vider la deuxième liste.

On va commencer par créer notre fenêtre :

 
Sélectionnez
import javax.swing.JComboBox;
//...
 
public class FenetreListes extends JFrame {
	private JComboBox liste1;
	private JComboBox liste2;
 
	//...
 
	private JPanel buildContentPane(){
		JPanel panel = new JPanel();
		panel.setLayout(new FlowLayout());
 
		Object[] elements = new Object[]{"Element 1", "Element 2", "Element 3", "Element 4", "Element 5"};
 
		liste1 = new JComboBox(elements);
 
		panel.add(liste1);
 
		JButton bouton = new JButton(new CopierAction(this, "Copier"));
 
		panel.add(bouton);
 
		liste2 = new JComboBox(elements);
 
		panel.add(liste2);
 
		JButton bouton2 = new JButton(new SupprimmerAction(this, "Enlever"));
 
		panel.add(bouton2);
 
		JButton bouton3 = new JButton(new ViderAction(this, "Vider"));
 
		panel.add(bouton3);
 
		return panel;
	}
 
	public JComboBox getListe1(){
		return liste1;
	}
 
	public JComboBox getListe2(){
		return liste2;
	}
}

Pour remplir la première liste, on a donné un tableau d'objets au constructeur du JComboBox qui va se charger de remplir la liste automatiquement avec les objets du tableau. Pour le reste rien de bien compliqué.

Ce qui devrait nous donner une interface de ce style :

Listes de choix
Listes de choix

On va maintenant créer nos 3 actions :

 
Sélectionnez
public class CopierAction extends AbstractAction {
	private FenetreAvecJComboBox fenetre;
 
	public CopierAction(FenetreAvecJComboBox fenetre, String texte){
		super(texte);
 
		this.fenetre = fenetre;
	}
 
	public void actionPerformed(ActionEvent e) { 
		Object selected = fenetre.getListe1().getSelectedItem();
		fenetre.getListe2().addItem(selected);
	} 
}
 
Sélectionnez
public class SupprimmerAction extends AbstractAction {
	private FenetreAvecJComboBox fenetre;
 
	public SupprimmerAction(FenetreAvecJComboBox fenetre, String texte){
		super(texte);
 
		this.fenetre = fenetre;
	}
 
	public void actionPerformed(ActionEvent e) { 
		Object selected = fenetre.getListe2().getSelectedItem();
		fenetre.getListe2().removeItem(selected);
	} 
}
 
Sélectionnez
public class ViderAction extends AbstractAction {
	private FenetreAvecJComboBox fenetre;
 
	public ViderAction(FenetreAvecJComboBox fenetre, String texte){
		super(texte);
 
		this.fenetre = fenetre;
	}
 
	public void actionPerformed(ActionEvent e) { 
		fenetre.getListe2().removeAllItems();
	} 
}

La non plus, rien de bien compliqué. On s'est contenté d'utiliser les méthodes de JComboBox pour faire nos modifications sur les listes.

On va maintenant passer à la seconde façon de faire.

Code complet :

7.1.2. Création d'un modèle de liste

Cette fois-ci, on ne va pas manipuler directement le JComboBox, mais son modèle. En fait, plusieurs composants Swing utilisent la notion de modèle pour stocker leurs données. La vue est donc complètement séparée des données qui sont stockés dans un modèle. En fait quand on utilise les méthodes de JComboBox, on va aussi modifier le modèle, mais par l'intermédiaire de JComboBox. Cette fois-ci, on va créer nos propres modèles.

On pourrait tout à fait se contenter d'utiliser les modèles par défaut, étant donné que ce qu'on va faire n'est pas très compliqué. Mais le but de ce tutoriel est de s'entraîner, il n'est donc pas superflu de découvrir comment coder ses propres modèles de listes.

Pour créer un modèle de liste, il suffit d'hériter de DefaultComboBoxModel. On peut aussi seulement implémenter ComboBoxModel, mais après, il faudra recoder une bonne partie qui est là-même à chaque fois.

Que doit faire le premier modèle ? Il ne doit rien faire de spécial. Il n'est pas modifiable et doit juste se contenter de contenir nos 5 éléments. Comme cette classe est destinée à contenir des chaînes de caractères, on va l'appeler StringModel.

Voilà à quoi va ressembler notre classe :

 
Sélectionnez
public class StringModel extends DefaultComboBoxModel {
	private ArrayList<String> strings;
 
	public StringModel(){
		super();
 
		strings = new ArrayList<String>();
	}
 
	public StringModel(String[] strings){
		super();
 
		this.strings = new ArrayList<String>();
 
		for(String string : strings){
			this.strings.add(string);
		}
	}
 
	protected ArrayList<String> getStrings(){
		return strings;
	}
 
	public String getSelectedString(){
		return (String)getSelectedItem();
	}
 
	@Override
	public Object getElementAt(int index) {
		return strings.get(index);
	}
 
	@Override
	public int getSize() {
		return strings.size();
	}
 
	@Override
	public int getIndexOf(Object element) {
		return strings.indexOf(element);
	}
}

Et voilà, rien de plus simple que ça ;) On s'est contenté de redéfinir les méthodes nécessaires pour leur dire d'aller chercher les éléments dans notre ArrayList.

On va maintenant passer au deuxième model. Celui-ci est modifiable, on va donc lui ajouter les méthodes addString, removeString et clearStrings. Comme il a les mêmes fonctionnalités de base, on va étendre StringModel et on va l'appeler ModifiableStringModel. Voilà ce que ça va donner :

 
Sélectionnez
public class ModifiableStringModel extends StringModel {
	public ModifiableStringModel(){
		super();
	}
 
	public ModifiableStringModel(String[] strings){
		super(strings);
	}
 
	public void clearStrings(){
		int oldSize = getStrings().size();
		getStrings().clear();
		fireIntervalRemoved(this, 0, oldSize);
	}
 
	public void addString(String string){
		getStrings().add(string);
		int size = getStrings().size();
		fireIntervalAdded(this, size, size);
	}
 
	public void removeString(String string){
		int position = getStrings().indexOf(string);
		getStrings().remove(position);
		fireIntervalRemoved(this, position, position);
	}
}

Cette fois-ci, les méthodes sont un peu compliquées. En fait, les méthodes fireXXX permettent d'avertir la liste déroulante qu'il y a eu du changement. Sinon, elle ne pourra pas savoir que les données ont changés. C'est le même principe que les écouteurs sur le bouton. Ici la liste écoute son modèle pour savoir quand elle doit se mettre à jour.

On va maintenant appliquer nos modèles. On va reprendre exactement le même code que pour la méthode précédente, mais cette fois, on va initialiser nos deux listes ainsi :

 
Sélectionnez
String[] strings = new String[]{"Element 1", "Element 2", "Element 3", "Element 4", "Element 5"};
 
liste1 = new JComboBox(new StringModel(strings));
 
//...
 
liste2 = new JComboBox(new ModifiableStringModel(strings));

On va également supprimmer les méthodes pour récupérer les JComboBox et les retourner par des méthodes qui nous retourne les modèles :

 
Sélectionnez
public StringModel getModeleListe1(){
	return (StringModel)liste1.getModel();
}
 
public ModifiableStringModel getModeleListe2(){
	return (ModifiableStringModel)liste2.getModel();
}

Et on peut maintenant passer à la modification de nos actions :

 
Sélectionnez
public class CopierAction extends AbstractAction {
	private FenetreAvecJComboBox fenetre;
 
	public CopierAction(FenetreAvecJComboBox fenetre, String texte){
		super(texte);
 
		this.fenetre = fenetre;
	}
 
	public void actionPerformed(ActionEvent e) { 
		String selected = fenetre.getModeleListe1().getSelectedString();
		fenetre.getModeleListe2().addString(selected);
	} 
}
 
Sélectionnez
public class SupprimmerAction extends AbstractAction {
	private FenetreAvecJComboBox fenetre;
 
	public SupprimmerAction(FenetreAvecJComboBox fenetre, String texte){
		super(texte);
 
		this.fenetre = fenetre;
	}
 
	public void actionPerformed(ActionEvent e) { 
		String selected = fenetre.getModeleListe2().getSelectedString();
		fenetre.getModeleListe2().removeString(selected);
	} 
}
 
Sélectionnez
public class ViderAction extends AbstractAction {
	private FenetreAvecJComboBox fenetre;
 
	public ViderAction(FenetreAvecJComboBox fenetre, String texte){
		super(texte);
 
		this.fenetre = fenetre;
	}
 
	public void actionPerformed(ActionEvent e) { 
		fenetre.getModeleListe2().clearStrings();
	} 
}

Rien de bien compliqué non plus de ce côté-là. On s'est juste contenté d'utiliser les méthodes qu'on a définies dans le modèle.

Comme vous le voyez, on a généré par mal de code pour pas grand chose. Les modèles sont plutôt à utiliser pour une liste qui est liée à une base de données ou à une autre classe, mais dans le cas qui nous préoccupe, ce n'est pas extrêmement utile. Néanmoins, vous pouvez aussi travailler toujours avec vos propres modèles si vous le désirez, ça offre quand même plus de souplesse.

Code complet :

7.2. Application au projet

Maintenant que l'on sait utiliser les listes déroulantes, on va les utiliser dans notre calculatrice pour offrir à l'utilisateur le choix de l'opérateur. Ce sera donc un JComboBox contenant +-*/ entre les 2 JTextField.

Bien que ça ne soit pas indispensables, on va créer un modèle de listes. C'est donc un modèle qui contiendra nos opérateurs, on va donc l'appeler OperateursModel :

 
Sélectionnez
public class OperateursModel extends DefaultComboBoxModel {
	private ArrayList<String> operateurs;
 
	public OperateursModel(){
		super();
 
		operateurs = new ArrayList<String>();
 
		operateurs.add("+");
		operateurs.add("-");
		operateurs.add("*");
		operateurs.add("/");
	}
 
	public String getSelectedOperateur(){
		return (String)getSelectedItem();
	}
 
	@Override
	public Object getElementAt(int index) {
		return operateurs.get(index);
	}
 
	@Override
	public int getSize() {
		return operateurs.size();
	}
 
	@Override
	public int getIndexOf(Object element) {
		return operateurs.indexOf(element);
	}
}

Rien de plus que ce qu'on a vu précédemment.

On va maintenant ajouter la liste déroulante à notre calculatrice :

 
Sélectionnez
//...
import javax.swing.JComboBox;
 
public class CalculatriceFenetre extends JFrame {
	private JComboBox liste;
 
	//...
 
	private JPanel buildContentPane(){
		JPanel panel = new JPane();
		panel.setLayout(new FlowLayout());
		panel.setBackground(Color.White);
 
		field1 = new JTextField();
 
		panel.add(field1);
 
		liste = new JComboBox(new OperateursModel());
		panel.add(liste);
 
		field2 = new JTextField();
 
		panel.add(field2);
 
		JButton bouton = new JButton(new CalculAction("Calculer"));
 
		panel.add(bouton);
 
		JLabel label = new JLabel("Résultat : Pas encore calculé");
 
		panel.add(label);
 
		return panel;
	}
 
	//...
 
	public OperateursModel getModelOperateurs(){
		return (OperateursModel)liste.getModel();
	}
}

Ce qui devrait vous donner finalement à l'affichage :

Notre calculatrice
Notre calculatrice

Cette fois, tous les éléments graphiques de notre calculatrice ont été codés.

Et maintenant on va coder notre action pour que notre calculatrice puisse enfin fonctionner :

 
Sélectionnez
public class CalculAction extends AbstractAction {
	private CalculatriceFenetre fenetre;
 
	public CalculAction(CalculatriceFenetre fenetre, String texte){
		super(texte);
 
		this.fenetre = fenetre;
	}
 
	public void actionPerformed(ActionEvent e) { 
		String nombre1String = fenetre.getField1().getText();//On récupère la valeur dans le premier champ
		double nombre1 = Double.parseDouble(nombre1String);//On convertit cette valeur en un nombre
 
		String nombre2String = fenetre.getField2().getText();//On récupère la valeur dans le deuxième champ
		double nombre2 = Double.parseDouble(nombre2String);//On convertit cette valeur en un nombre
 
		String operateur = fenetre.getModelOperateurs().getSelectedOperateur();
 
		double resultat = 0;
 
		if("+".equals(operateur)){
			resultat = nombre1 + nombre2;
		} else if ("-".equals(operateur)){
			resultat = nombre1 - nombre2;
		} else if ("*".equals(operateur)){
			resultat = nombre1 * nombre2; 
		} else if ("/".equals(operateur)){
			resultat = nombre1 / nombre2; 
		}
 
		fenetre.getLabel().setText("Résultat = " +  resultat);
	} 
}

Rien de très compliqué. On commence par récupérer les 2 nombres saisis dans les champs textes, on les convertit en double, puis on récupére l'opérateur et en fonction de l'opérateur, on éxécute le calcul et on écrit le résultat dans le label de l'interface.

En théorie, toute la partie de calcul devrait être dans une classe à part et l'action ne devrait faire que récupérer les éléments de l'interface graphique, d'envoyer les deux nombres et l'opérateur à la classe de calcul et afficher le résultat que cette dernière lui aura retournée. Mais dans un petit projet comme le nôtre, il n'est pas indispensable de le faire. Par contre, cela devient utile dès que l'on fait un projet de taille moyenne voire grande.

Et voilà ! Notre calculatrice est maintenant fonctionnelle.

Résultat :

Notre calculatrice
Notre calculatrice

On pourrait bien sûr l'améliorer. On pourrait voir s'il y a une division par zéro, on pourrait vider les champs textes après la récupération des valeurs pour une autre utilisation. Mais comme maintenant vous vous y connaissez, essayez. La meilleure façon de progresser est de pratiquer.

Code complet :


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2009 Baptiste Wicht. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.