Cours Swing
Cours Swing
Doudoux
FORUMS TUTORIELS MAGAZINE FAQ BLOGS CHAT NEWSLETTER ÉTUDES EMPLOI CLUB
ALM Java .NET Dév. Web EDI Programmation SGBD Office Solutions d'entreprise Applications Mobiles Systèmes
Forums Java FAQ Java Tutoriels Java Livres Java Vidéos Java Sources Java Outils, EDI & API Java JavaSearch
Niveau : Intermédiaire
Swing fait partie de la bibliothèque Java Foundation Classes (JFC). C'est une API dont le but est similaire à celui de l'API AWT mais dont les modes
de fonctionnement et d'utilisation sont complètement différents. Swing a été intégré au JDK depuis sa version 1.2. Cette bibliothèque existe
séparément. pour le JDK 1.1.
l'API Swing : de nouvelles classes et interfaces pour construire des interfaces graphiques
Accessibility API :
2D API: support du graphisme en 2D
API pour l'impression et le cliquer/glisser
La présentation de Swing
Les packages Swing
Un exemple de fenêtre autonome
Les composants Swing
Les boutons
Les composants de saisie de texte
Les onglets
Le composant JTree
Les menus
L'affichage d'une image dans une application.
Tous les éléments de Swing font partie d'un package qui a changé plusieurs fois de nom : le nom du package dépend de la version du J.D.K.
utilisée :
[Link] : jusqu'à la version 1.1 beta 2 de Swing, de la version 1.1 des JFC et de la version 1.2 beta 4 du J.D.K.
[Link] : utilisé par le J.D.K. 1.2 beta 2 et 3
[Link] : à partir des versions de Swing 1.1 beta 3 et J.D.K. 1.2 RC1
Les composants Swing forment un nouvelle hiérarchie parallèle à celle de l'AWT. L'ancêtre de cette hiérarchie est le composant JComponent.
Presque tous ses composants sont écrits en pur Java : ils ne possèdent aucune partie native sauf ceux qui assurent l'interface avec le système
d'exploitation : JApplet, JDialog, JFrame, et JWindow. Cela permet aux composants de toujours avoir la même apparence quelque soit le système
sur lequel l'application s'exécute.
[Link] 1/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
ce sont des beans
ce sont des composants légers (pas de partie native) hormis quelques exceptions.
leurs bords peuvent être changés
La procédure à suivre pour utiliser un composant Swing est identique à celle des composants de la bibliothèque AWT : créer le composant en
appelant son constructeur, appeler les méthodes du composant si nécessaire pour le personnaliser et l'ajouter dans un conteneur.
Swing utilise la même infrastructure de classes qu'AWT, ce qui permet de mélanger des composants Swing et AWT dans la même interface. Il est
toutefois recommandé d'éviter de les utiliser simultanément car certains peuvent ne pas être restitués correctement.
Les composants Swing utilisent des modèles pour contenir leurs états ou leurs données. Ces modèles sont des classes particulières qui possèdent
toutes un comportement par défaut.
import [Link].*;
import [Link].*;
public swing1() {
super("titre de l'application");
[Link] 2/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
WindowListener l = new WindowAdapter() {
public void windowClosing(WindowEvent e){
[Link](0);
}
};
addWindowListener(l);
setSize(200,100);
setVisible(true);
}
Le constructeur d'un objet Icon admet comme seul paramètre le nom ou l'URL d'un fichier graphique
import [Link].*;
import [Link].*;
public swing3() {
super("titre de l'application");
addWindowListener(l);
Constructeur Rôle
JFrame()
Par défaut, la fenêtre créée n'est pas visible. La méthode setVisible() permet de l'afficher.
import [Link].*;
[Link] 3/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
La gestion des événements est identique à celle utilisée dans l'AWT depuis le J.D.K. 1.1.
import [Link].*;
import [Link].*;
public swing2() {
super("titre de l'application");
setContentPane(panneau);
setSize(200,100);
setVisible(true);
}
Tous les composants associés à un objet JFrame sont gérés par un objet de la classe JRootPane. Un objet JRootPane contient plusieurs Panes.
Tous les composants ajoutés au JFame doivent être ajoutés à un des Pane du JRootPane et non au JFrame directement. C'est aussi à un de ces
Panes qu'il faut associer un layout manager si nécessaire.
Le Pane le plus utilisé est le ContentPane. Le Layout manager par défaut du contentPane est BorderLayout. Il est possible de le changer :
...
[Link]().setLayout(new FlowLayout());
...
import [Link].*;
Le glassPane est un JPanel transparent qui se situe au-dessus du layeredPane. Le glassPane peut être n'importe quel composant : pour le modifier
il faut utiliser la méthode setGlassPane() en fournissant le composant en paramètre.
Le contentPane est par défaut un JPanel opaque dont le gestionnaire de présentation est un BorderLayout. Ce panel peut être remplacé par
n'importe quel composant grâce à la méthode setContentPane().
[Link] 4/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
import [Link].*;
import [Link].*;
Résultat :
C:\swing\code>java TestJFrame7
Exception in thread "main" [Link]: Do not use [Link]
out() use [Link]().setLayout() instead
at [Link](Unknown Source)
at [Link](Unknown Source)
at [Link]([Link])
Le menuBar permet d'attacher un menu à la JFrame. Par défaut, le menuBar est vide. La méthode setJMenuBar() permet d'affecter un menu à la
JFrame.
import [Link].*;
import [Link].*;
[Link](true);
}
}
Il est possible de préciser comment un objet JFrame, JInternalFrame, ou JDialog réagit à sa fermeture grâce à la méthode
setDefaultCloseOperation(). Cette méthode attend en paramètre une valeur qui peut être :
Constante Rôle
Cette méthode ne permet pas d'associer d'autres traitements. Dans ce cas, il faut intercepter l'événement et lui associer les traitements.
Exemple ( code Java 1.1 ) : la fenêtre disparaît lors de sa fermeture mais l'application ne se termine pas.
import [Link].*;
[Link](WindowConstants.DISPOSE_ON_CLOSE);
[Link](true);
}
}
[Link] 5/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
[Link]. La personnalisation de l'icône
import [Link].*;
}
}
Si l'image n'est pas trouvée, alors l'icône est vide. Si l'image est trop grande, elle est redimensionnée.
Par défaut, une JFrame est affichée dans le coin supérieur gauche de l'écran. Pour la centrer dans l'écran, il faut procéder comme pour une
Frame : déterminer la position de la Frame en fonction de sa dimension et de celle de l'écran et utiliser la méthode setLocation() pour affecter
cette position.
import [Link].*;
import [Link].*;
[Link](WindowConstants.DISPOSE_ON_CLOSE);
[Link](true);
}
}
La gestion des événements associés à un objet JFrame est identique à celle utilisée pour un objet de type Frame de AWT.
import [Link].*;
import [Link].*;
}
}
Constructeurs Rôle
Le composant JLabel permet d'afficher un texte et/ou une icône en précisant leur alignement. L'icône doit être au format GIF et peut être une
animation dans ce format.
import [Link].*;
import [Link].*;
[Link]().add(pannel);
[Link](true);
}
}
Méthodes Rôle
[Link] 7/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
setVerticalTextAlignment() Permet de modifier l'alignement vertical du texte uniquement
Exemple :
[Link]([Link]);
L'alignement vertical par défaut d'un JLabel est centré. L'alignement horizontal par défaut est soit à droite s'il ne contient que du texte, soit centré
s'il contient une image avec ou sans texte. Pour modifier cet alignement, il suffit d'utiliser les méthodes ci-dessus en utilisant des constantes en
paramètres : [Link], [Link], [Link], [Link], [Link]
Par défaut, un JLabel est transparent : son fond n'est pas dessiné. Pour le dessiner, il faut utiliser la méthode setOpaque() :
import [Link].*;
import [Link].*;
[Link](100,200);
JPanel pannel = new JPanel();
[Link]().add(pannel);
[Link](true);
}
}
Dans l'exemple, les 2 JLabel ont le fond rouge demandé par la méthode setBackground(). Seul le deuxième affiche un fond rouge car il est rendu
opaque avec la méthode setOpaque().
Il est possible d'associer un raccourci clavier au JLabel qui permet de donner le focus à un autre composant. La méthode setDisplayedMnemonic()
permet de définir le raccourci clavier. Celui-ci sera activé en utilisant la touche Alt avec le caractère fourni en paramètre. La méthode
setLabelFor() permet d'associer le composant fourni en paramètre au raccourci.
import [Link].*;
import [Link].*;
[Link]().add(pannel);
[Link](true);
}
}
Dans l'exemple, à l'ouverture de la fenêtre, le focus est sur le bouton. Un appui sur Alt+'n' donne le focus au champ de saisie.
La classe JPanel est un conteneur utilisé pour regrouper et organiser des composants grâce à un gestionnaire de présentation (layout manager).
Le gestionnaire par défaut d'un JPanel est un objet de la classe FlowLayout.
[Link] 8/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
Méthode Rôle
[Link] 9/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
Il est possible de préciser une image différente lors du passage de la souris sur le composant et lors de l'enfoncement du bouton : dans ce cas, il
faut créer trois images pour chacun des états (normal, enfoncé et survolé). L'image normale est associée au bouton grâce au constructeur,
l'image enfoncée grâce à la méthode setPressedIcon() et l'image lors d'un survol grâce à la méthode setRolloverIcon(). Il suffit enfin d'appeler la
méthode setRolloverEnable() avec en paramètre la valeur true.
import [Link].*;
import [Link].*;
public swing4() {
super("titre de l'application");
Un bouton peut recevoir des événements de type ActionEvents (le bouton a été activé), ChangeEvents, et ItemEvents.
import [Link].*;
import [Link].*;
[Link](bouton1);
[Link]().add(pannel);
[Link](true);
}
}
Pour de plus amples informations sur la gestion des événements, voir le chapitre correspondant.
Constructeur Rôle
JButton()
JButton(String) préciser le texte du bouton
JButton(Icon) préciser une icône
JButton(String, Icon) préciser un texte et une icône
Il ne gère pas d'état. Toutes les indications concernant le contenu du composant JLabel sont valables pour le composant JButton.
[Link] 10/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
Exemple ( code Java 1.1 ) : un bouton avec une image
import [Link].*;
import [Link].*;
public swing3() {
super("titre de l'application");
Dans un conteneur de type JRootPane, il est possible de définir un bouton par défaut grâce à sa méthode setDefaultButton().
Exemple ( code Java 1.1 ) : définition d'un bouton par défaut dans un JFrame
import [Link].*;
import [Link].*;
[Link]().add(pannel);
[Link]().setDefaultButton(bouton3);
[Link](true);
}
}
Le bouton par défaut est activé par un appui sur la touche Entrée alors que le bouton actif est activé par un appui sur la barre d'espace.
La méthode isDefaultButton() de JButton permet de savoir si le composant est le bouton par défaut.
La méthode setSelected() héritée de AbstractButton permet de mettre à jour l'état du bouton. La méthode isSelected() permet de connaître cet
état.
La classe ButtonGroup permet de gérer un ensemble de boutons en garantissant qu'un seul bouton du groupe sera sélectionné.
Pour utiliser la classe ButtonGroup, il suffit d'instancier un objet et d'ajouter des boutons (objets héritant de la classe AbstractButton) grâce à la
méthode add(). Il est préférable d'utiliser des objets de la classe JToggleButton ou d'une de ses classes filles car elles sont capables de gérer leurs
états.
import [Link].*;
[Link] 11/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
[Link]().add(pannel);
[Link](true);
}
}
Constructeur Rôle
Un groupe de cases à cocher peut être défini avec la classe ButtonGroup. Dans ce cas, un seul composant du groupe peut être sélectionné. Pour
l'utiliser, il faut créer un objet de la classe ButtonGroup et utiliser la méthode add() pour ajouter un composant au groupe.
import [Link].*;
[Link]().add(pannel);
[Link](true);
}
}
Un objet de type JRadioButton représente un bouton radio d'un groupe de boutons . A un instant donné, un seul des boutons radio associés à un
même groupe peut être sélectionné. La classe JRadioButton hérite de la classe AbstractButton.
Un bouton radio possède un libellé et éventuellement une icône qui peut être précisée, pour chacun des états du bouton, en utilisant les
méthodes setIcon(), setSelectedIcon() et setPressedIcon().
import [Link].*;
[Link] 12/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
JRadioButton bouton1 = new JRadioButton("Bouton 1");
[Link](bouton1);
JRadioButton bouton2 = new JRadioButton("Bouton 2");
[Link](bouton2);
JRadioButton bouton3 = new JRadioButton("Bouton 3");
[Link](bouton3);
[Link]().add(pannel);
[Link](true);
}
}
Constructeur Rôle
JRadioButton(String, Icon, boolean) Créer un bouton avec le libellé, l'icône et l'état fournis en paramètres
Il faut ajouter tous les JRadioButton du groupe en utilisant la méthode add() de la classe ButtonGroup. Lors de la sélection d'un bouton, c'est
l'objet de type ButtonGroup qui se charge de déselectionner le bouton précédemment sélectionné dans le groupe.
Exemple :
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
[Link](JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new GridLayout(0,1));
Border border = [Link]("Sélection");
[Link](border);
ButtonGroup group = new ButtonGroup();
JRadioButton radio1 = new JRadioButton("Choix 1", true);
JRadioButton radio2 = new JRadioButton("Choix 2");
JRadioButton radio3 = new JRadioButton("Choix 3");
[Link](radio1);
[Link](radio1);
[Link](radio2);
[Link](radio2);
[Link](radio3);
[Link](radio3);
Container contentPane = [Link]();
[Link](panel, [Link]);
[Link](300, 150);
[Link](true);
}
}
[Link] 13/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
Exemple :
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
[Link](radio1);
[Link](radio1);
[Link](radio2);
[Link](radio2);
[Link](radio3);
[Link](radio3);
Lors de la sélection d'un bouton du groupe, il y a plusieurs événements qui peuvent être émis :
Exemple :
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
[Link] 14/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
TestJRadioButton app = new TestJRadioButton();
[Link]();
}
[Link](JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new GridLayout(0, 1));
Border border = [Link]("Sélection");
[Link](border);
ButtonGroup group = new ButtonGroup();
[Link](radio1);
[Link](radio1);
[Link](radio2);
[Link](radio2);
[Link](radio3);
[Link](radio3);
[Link](this);
[Link](this);
[Link](this);
[Link](this);
[Link](this);
[Link](this);
@Override
public void actionPerformed(ActionEvent e) {
[Link]("Clic sur le bouton : " + [Link]());
}
@Override
public void itemStateChanged(ItemEvent e) {
[Link]("Bouton " + ((JRadioButton) [Link]()).getActionCommand());
if ([Link]() == [Link])
[Link](" deselectionne");
if ([Link]() == [Link])
[Link](" selectionne");
}
}
La méthode getSelection() de la classe ButtonGroup renvoie le modèle du bouton radio sélectionné encapsulé dans un objet de type ButtonModel.
Pour déterminer le bouton sélectionné, il faut parcourir les boutons du groupe et comparer leurs modèles.
Exemple :
[Link] 15/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
La classe abstraite JTextComponent est la classe mère de tous les composants permettant la saisie de texte.
Les données du composant (le modèle dans le motif de conception MVC) sont encapsulées dans un objet qui implémente l'interface Document.
Deux classes implémentant cette interface sont fournies en standard : PlainDocument pour du texte simple et StyledDocument pour du texte
riche pouvant contenir entre autres plusieurs polices de caractères, des couleurs, des images, ...
Méthode Rôle
void copy() Copier le contenu du texte et le mettre dans le presse papier système
void cut() Couper le contenu du texte et le mettre dans le presse papier système
Document getDocument() Renvoyer l'objet de type Document qui encapsule le texte saisi
String getSelectectedText() Renvoyer le texte sélectionné dans le composant
int getSelectionEnd() Renvoyer la position de la fin de la sélection
int getSelectionStart() Renvoyer la position du début de la sélection
String getText() Renvoyer le texte saisi
Renvoyer une portion du texte débutant à partir de la position donnée
String getText(int, int) par le premier paramètre et la longueur donnée dans le second
paramètre
bool isEditable() Renvoyer un booléen qui précise si le texte est éditable ou non
void paste() Coller le contenu du presse papier système dans le composant
Sélectionner une portion du texte dont les positions de début et de fin
void select(int,int)
sont fournies en paramètres
void setCaretPosition(int) Déplacer le curseur dans le texte à la position précisé en paramètre
void setEditable(boolean) Permet de préciser si les données du composant sont éditables ou non
void setSelectionEnd(int) Modifier la position de la fin de la sélection
void setSelectionStart(int) Modifier la position du début de la sélection
void setText(String) Modifier le contenu du texte
Toutes ces méthodes sont donc accessibles grâce à l'héritage pour tous les composants de saisie de texte proposés par Swing.
import [Link].*;
[Link] 16/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
JTextField testField1 = new JTextField ("mon texte");
[Link](testField1);
[Link]().add(pannel);
[Link](true);
}
}
La propriété horizontalAligment permet de préciser l'alignement du texte dans le composant en utilisant les valeurs [Link] ,
[Link] ou [Link].
La classe JPasswordField permet la saisie d'un texte dont tous les caractères saisis seront affichés sous la forme d'un caractère particulier ('*' par
défaut). Cette classe hérite de la classe JTextField.
import [Link];
import [Link].*;
[Link](passwordField1);
[Link]().add(pannel);
[Link](true);
}
}
La méthode setEchoChar(char) permet de préciser le caractère qui sera montré lors de la saisie.
Il ne faut pas utiliser la méthode getText() qui est déclarée deprecated mais la méthode getPassword() pour obtenir la valeur du texte saisi.
import [Link];
import [Link].*;
import [Link].*;
[Link](bouton1);
[Link]().add(pannel);
[Link](true);
}
Les méthodes copy() et cut() sont redéfinies pour n'émettre qu'un bip. Elles empêchent l'exportation du contenu du champ.
[Link] 17/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
38.6.4. La classe JFormattedTextField
Le JDK 1.4 propose la classe JFormattedTextField pour faciliter la création d'un composant de saisie personnalisé. Cette classe hérite de la classe
JTextField.
Exemple ( code Java 1.1 ) : affichage de la page de Google avec gestion des hyperliens
import [Link];
import [Link].*;
import [Link].*;
try {
editeur = new JEditorPane(new URL("[Link]
[Link](false);
[Link](new HyperlinkListener() {
public void hyperlinkUpdate(HyperlinkEvent e) {
if ([Link]() == [Link]) {
URL url = [Link]();
if (url == null)
return;
try {
[Link]([Link]());
} catch (Exception ex) {
[Link]();
}
}
}
});
[Link](editeur);
} catch (Exception e1) {
[Link]();
}
JFrame f = new JFrame("ma fenetre");
[Link](500, 300);
[Link]().add(pannel);
[Link](true);
}
}
La suite de cette section sera développée dans une version future de ce document
[Link] 18/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
38.6.7. La classe JTextArea
La classe JTextArea est un composant qui permet la saisie de texte simple en mode multiligne. Le modèle utilisé par ce composant est le
PlainDocument : il ne peut donc contenir que du texte brut sans éléments multiples de formatage.
JTexteArea propose plusieurs méthodes pour ajouter du texte dans son modèle :
La méthode replaceRange() permet de remplacer la partie de texte occupant les index donnés en paramètres par la chaîne fournie.
La propriété rows permet de définir le nombre de lignes affichées par le composant : cette propriété peut donc être modifiée lors d'un
redimensionnement du composant. La propriété lineCount en lecture seule permet de savoir le nombre de lignes qui composent le texte. Il ne faut
pas confondre ces deux propriétés.
import [Link].*;
[Link](textArea1);
[Link]().add(pannel);
[Link](true);
}
}
Par défaut, la taille du composant augmente au fur et à mesure de l'augmentation de la taille du texte qu'il contient. Pour éviter cet effet, il faut
encapsuler le JTextArea dans un JScrollPane.
import [Link];
import [Link].*;
import [Link];
[Link] 19/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
import [Link];
import [Link].*;
[Link](true);
[Link](onglets);
[Link]().add(pannel);
[Link](true);
}
}
A partir du JDK 1.4, il est possible d'ajouter un raccourci clavier sur chacun des onglets en utilisant la méthode setMnemonicAt(). Cette méthode
attend deux paramètres : l'index de l'onglet concerné (le premier commence à 0) et la touche du clavier associée sous la forme d'une constance
KeyEvent.VK_xxx. Pour utiliser ce raccourci, il suffit d'utiliser la touche désignée en paramètre de la méthode avec la touche Alt.
La classe JTabbedPane possède plusieurs méthodes qui permettent de définir le contenu de l'onglet :
Méthodes Rôles
Permet d'ajouter un nouvel onglet dont le titre et le composant sont
addTab(String, Component) fournis en paramètres. Cette méthode possède plusieurs surcharges
qui permettent de préciser une icône et une bulle d'aide
Permet d'insérer un onglet dont la position est précisée dans le dernier
insertTab(String, Icon, Component, String, index)
paramètre
remove(int) Permet de supprimer l'onglet dont l'index est fourni en paramètre
Permet de préciser le positionnement des onglets dans le composant
setTabPlacement JTabbedPane. Les valeurs possibles sont les constantes TOP, BOTTOM,
LEFT et RIGHT définies dans la classe JTabbedPane.
La méthode getSelectedIndex() permet d'obtenir l'index de l'onglet courant. La méthode setSelectedIndex() permet de définir l'onglet courant.
Au premier abord, le composant JTree peut sembler compliqué à mettre en oeuvre mais la compréhension de son mode de fonctionnement peut
grandement faciliter son utilisation.
Il utilise le modèle MVC en proposant une séparation des données (data models) et du rendu de ces données (cell renderers).
Dans l'arbre, les éléments qui ne possèdent pas d'élément fils sont des feuilles (leaf). Chaque élément est associé à un objet (user object) qui va
permettre de déterminer le libellé affiché dans l'arbre en utilisant la méthode toString().
[Link] 20/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
public JTree();
public JTree(Hashtable value);
public JTree(Vector value);
public JTree(Object[] value);
public JTree(TreeModel model);
public JTree(TreeNode rootNode);
public JTree(TreeNode rootNode, boolean askAllowsChildren);
Lorsqu'une instance de JTree est créée avec le constructeur par défaut, l'arbre obtenu contient des données par défaut.
import [Link];
import [Link];
public TestJtree() {
super();
initialize();
}
Les trois constructeurs qui attendent en paramètre une collection permettent de créer un arbre avec une racine non affichée qui va contenir
comme noeuds fils directs tous les éléments contenus dans la collection.
Dans ce cas, la racine n'est pas affichée. Pour l'afficher, il faut utiliser la méthode setRootVisible()
[Link](true);
[Link] 21/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
Dans ce cas elle se nomme root et possède un commutateur qui permet de refermer ou d'étendre la racine. Pour supprimer ce commutateur, il
faut utiliser la méthode [Link](false)
L'utilisation de l'une ou l'autre des collections n'est pas équivalente. Par exemple, l'utilisation d'une hashtable ne garantit pas l'ordre des noeuds
puisque par définition cette collection ne gère pas un ordre précis.
Généralement, la construction d'un arbre utilise un des constructeurs qui attend en paramètre un objet de type TreeModel ou TreeNode car ces
deux objets permettent d'avoir un contrôle sur l'ensemble des données de l'arbre. Leur utilisation sera détaillée dans la section consacrée à la
gestion des données de l'arbre.
En fonction du nombre d'éléments et de l'état étendu ou non d'un ou plusieurs éléments, la taille de l'arbre peut varier : il est donc nécessaire
d'inclure le composant JTree dans un composant JScrollPane
...
...
...
L'utilisateur peut sélectionner un noeud en cliquant sur son texte ou son icône. Un double clic sur le texte ou l'icône d'un noeud permet de
l'étendre ou le refermer selon son état.
En application du modèle MVC, le composant JTree ne gère pas directement chaque noeud et la façon dont ceux-ci sont organisés et stockés mais
il utilise un objet dédié de type TreeModel.
Ainsi, comme dans d'autres composants Swing, le composant JTree manipule des objets implémentant des interfaces. Une classe qui encapsule
les données de l'arbre doit implémenter l'interface TreeModel. Chaque noeud de l'arbre doit implémenter l'interface TreeNode.
Pour préciser les données contenues dans l'arbre, il faut créer un objet qui va encapsuler ces données et les passer au constructeur de la classe
Jtree. Cet objet peut être de type TreeNode ou TreeModel. Un TreeModel stocke les données de chaque noeud dans un objet de type TreeNode.
[Link] 22/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
Généralement, le plus simple est de définir un type TreeNode personnalisé. Swing propose pour cela l'objet DefaultMutableTreeNode. il suffit d'en
créer une instance pour stocker les données et l'utiliser lors de l'appel du constructeur de la classe JTree.
La classe DefaultMutableTreeNode implémente l'interface MutableTreeNode qui elle-même hérite de l'interface TreeNode
import [Link];
...
if (jTree == null) {
DefaultMutableTreeNode racine = new DefaultMutableTreeNode("Racine de l'arbre");
DefaultMutableTreeNode noeud1 = new DefaultMutableTreeNode("Noeud 1");
[Link](noeud1);
DefaultMutableTreeNode noeud2 = new DefaultMutableTreeNode("Noeud 2");
[Link](noeud2);
jTree = new JTree(racine);
}
return jTree;
}
Dans ce cas, une instance de la classe DefaultTreeModel est créée avec la racine fournie en paramètre du constructeur de la classe JTree.
Une autre solution permet de créer une instance de la classe DefaultTreeModel et de la passer en paramètre du constructeur de la classe JTree.
Chaque noeud de l'arbre stocké dans le modèle de données implémente l'interface TreeNode.
Cette interface définit 7 méthodes dont la plupart concernent les relations entre les noeuds :
Méthode Rôle
Enumeration children() renvoie une collection des noeuds fils
boolean getAllowsChildren() Renvoie un booléen qui precise si le noeud peut avoir des noeuds fils
TreeNode getChildAt(int index) Renvoie le noeud fils correspondant à l'index fourni en paramètre
int getChildCount() renvoie le nombre de noeuds fils directs du noeud
int getIndex(TreeNode child) renvoie l'index du noeud passé en paramètre
TreeNode getParent() renvoie le noeud père
boolean isLeaf() renvoie un booléen qui précise si le noeud est une feuille
Chaque noeud ne peut avoir qu'un seul père (hormis le noeud racine qui ne possède pas de père) et autant de noeuds fils que souhaité. La
méthode getParent() permet de renvoyer le noeud père. Elle renvoie null lorsque cette méthode est appelée sur le noeud racine.
La méthode getAllowsChildren() permet de préciser si le noeud peut avoir des noeuds enfants : si elle renvoie false alors le noeud sera toujours
une feuille et ne pourra donc jamais avoir de noeuds fils.
La méthode isLeaf() renvoie un booléen précisant si le noeud est une feuille ou non. Une feuille est un noeud qui ne possède pas de noeud fils.
Les noeuds fils sont ordonnés car l'ordre de représentation des données peut être important dans la représentation de données hiérarchiques. La
méthode getChildAt() renvoie le noeud fils dont l'index est fourni en paramètre de la méthode. La méthode getIndex() renvoie l'index du noeud
fils passé en paramètre.
Les 7 méthodes définies par l'interface TreeNode ne permettent que de lire des valeurs. Pour mettre à jour un noeud, il est nécéssaire d'utiliser
l'interface MutableTreeNode qui hérite de la méthode TreeNode. Elle définit en plus plusieurs méthodes permettant de mettre à jour le noeud.
La méthode insert() permet d'ajouter le noeud fourni en paramètre comme noeud fils à la position précisée par le second paramètre.
Il existe deux surcharges de la méthode remove() qui permettent de déconnecter un noeud fils de son père. La première surcharge attend en
paramètre l'index du noeud fils. La seconde surcharge attend en paramètre le noeud à déconnecter. Dans tous les cas, il est nécessaire d'utiliser
cette méthode sur le noeud père.
La méthode removeFromParent() appelée à partir d'un noeud permet de supprimer le lien entre le noeud et son père.
[Link] 23/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
La méthode setParent() permet de préciser le père du noeud.
La méthode setUserObject() permet d'associer un objet au noeud. L'appel à la méthode toString() de cet objet permettra de déterminer le libellé
du noeud qui sera affiché.
Généralement, les noeuds créés dans le modèle sont des instances de la classe DefaultMutableTreeNode. Cette classe implémente l'interface
MutableTreeNode ce qui permet d'obtenir une instance d'un noeud modifiable.
Le plus souvent, les noeuds fournis en paramètres des méthodes proposées par Swing sont de type TreeNode. Si l'instance du noeud est de type
DefaultTreeNode, il est possible de faire un cast pour accéder à toutes ses méthodes.
La classe propose trois constructeurs dont deux attendent en paramètre l'objet qui sera associé au noeud. L'un des deux attend en plus un
booléen qui permet de préciser si le noeud peut avoir des noeuds fils.
Constructeur Rôle
Créer un noeud sans objet associé. Cette association pourra être
public DefaultMutableTreeNode()
faite avec la méthode setObject()
Créer un noeud en précisant l'objet qui lui sera associé et qui pourra
public DefaultMutableTreeNode(Object userObject)
avoir des noeuds fils
public DefaultMutableTreeNode(Object userObject, boolean
Créer un noeud dont le booléen précise s'il pourra avoir des fils
allowsChildren)
Pour ajouter une instance de la classe DefaultMutableTreeNode dans le modèle de l'arbre, il est possible d'utiliser la méthode insert() de l'interface
MutuableTreeNode ou utiliser la méthode add() de la classe DefaultMutableTreeNode. Celle-ci attend en paramètre une instance du noeud fils à
ajouter. Elle ajoute le noeud après le dernier noeud fils, ce qui évite d'avoir à garder une référence sur la position où insérer le noeud.
Il est aussi possible de définir sa propre classe qui implémente l'interface MutableTreeNode : une possibilité est de définir une classe fille de la
classe DefaultMutableTreeNode.
Les modifications du contenu de l'arbre peuvent se faire au niveau du modèle (DefaultTreeModel) ou au niveau du noeud.
La méthode getModel() de la classe JTree permet d'obtenir une référence sur l'instance de la classe TreeModel qui encapsule le modèle de
données.
Il est ainsi possible d'accéder à tous les noeuds du modèle pour les modifier.
L'interface TreeModel ne propose rien pour permettre la mise à jour du modèle. Pour cela, il faut utiliser une instance de la classe
DefaultTreeModel.
L'avantage de ces deux méthodes est qu'elles mettent à jour le modèle mais aussi qu'elles mettent à jour la vue en appelant respectivement les
méthodes nodesWereInserted() et nodesWereRemoved() de la classe DefaultTreeModel.
Ces deux méthodes sont donc pratiques pour faire des mises à jour mineures mais elles sont peut adaptées pour de nombreuses mises à jour
puisqu'elles déclenchent un événement à chacune de leurs utilisations.
La classe DefaultMutableTreeNode propose plusieurs méthodes pour mettre à jour le modèle à partir du noeud qu'elle encapsule.
Toutes ces méthodes sauf la dernière agissent sur un ou plusieurs noeuds fils. Ces méthodes agissent simplement sur la structure du modèle.
Elles ne provoquent pas un affichage par la partie vue de ces changements. Pour cela il est nécessaire d'utiliser une des méthodes suivantes
proposées par la classe DefaultTreeModel :
Méthode Rôle
void reload() rafraichir toute l'arborescence à partir du modèle
rafraichir toute l'arborescence à partir du noeud précisé en
void reload(TreeNode node)
paramètre
pour le noeud précisé, cette méthode rafraichit les noeuds fils
void nodesWereInserted(TreeNode node, int[] childIndices)
ajoutés dont les index sont fournis en paramètre
void nodesWereRemoved(TreeNode node,int[] childIndices, Object[] pour le noeud précisé, cette méthode rafraichit les noeuds fils
removedChildren) supprimés dont les index sont fournis en paramètre
void nodeStructureChanged(TreeNode node) cette méthode est identique à la méthode reload()
Il est possible d'enregistrer un listener de type TreeModelListener sur un objet de type DefaultTreeModel.
L'interface TreeModelListener définit quatre méthodes pour répondre à des événements particuliers :
Méthode Rôle
Toutes ces méthodes ont un objet de type TreeModelEvent qui encapsule l'événement.
La classe TreeModelEvent propose cinq méthodes pour obtenir des informations sur les noeuds impactés par l'événement.
Méthode Rôle
renvoie une instance sur le modèle de l'arbre (généralement un objet
Object getSource()
de type DefaultTreeModel)
TreePath getTreePath() renvoie le chemin du noeud affecté par l'événement
Renvoie la succession de noeuds de la racine au noeud parent des
Object[] getPath()
noeuds impactés
Object[] getChildren()
int[] getChildIndices() retourne les index des noeuds modifiés
Dans la méthode treeStructureChanged(), seules les méthodes getPath() et getTreePath() fournissent des informations utiles en retournant le
noeud qui a été modifié.
Dans la méthode treeNodesChanged(), treeNodesRemoved() et treeNodesInserted() les méthodes getPath() et getTreePath() renvoient le noeud
père des noeuds affectés. Les méthodes getChildIndices() et getChidren() renvoient respectivement un tableau des index des noeuds fils modifiés
et un tableau de ces noeuds fils.
Dans ces méthodes, les méthodes getPath() et getTreePath() renvoient le noeud père des noeuds affectés.
Comme l'objet JTree enregistre ses propres listeners, il n'est pas nécessaire la plupart du temps, d'enregistrer ces listeners hormis pour des
besoins spécifiques.
Par défaut, le composant JTree est readonly. Il est possible d'autoriser l'utilisateur à modifier le libellé des noeuds en utilisant la méthode
setEditable() avec le paramètre true : [Link](true);
sur un noeud non sélectionné : cliquer rapidement trois fois sur le noeud à modifier
sur un noeud déjà sélectionné : cliquer une fois sur le noeud ou appuyer sur la touche F2
[Link] 25/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
Pour valider les modifications, il suffit d'appuyer sur la touche « Entree ».
Il est possible d'enregistrer un listener de type TreeModelListerner pour assurer des traitements lors d'événements liés à l'édition d'un noeud.
L'interface TreeModelListener définit la méthode treeNodesChanged() qui permet de traiter les événements de type TreeModelEvent liés à la
modification d'un noeud.
[Link](true);
[Link]().addTreeModelListener(new TreeModelListener() {
});
Il est possible de définir un éditeur particulier pour éditer la valeur d'un noeud. Un éditeur particulier doit implémenter l'interface TreeCellEditor.
Cette interface hérite de l'interface CellEditor qui définit plusieurs méthodes utiles pour la création d'un éditeur dédié :
Object getCellEditorValue();
boolean isCellEditable(EventObject);
boolean shouldSelectCell(EventObject);
boolean stopCellEditing();
void cancelCellEditing();
void addCellEditorListener( CellEditorListener);
void removeCellEditorListener( CellEditorListener);
Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row);
La valeur initiale est fournie dans le second paramètre de type Object. Les trois arguments de type booléen suivants permettent respectivement
de savoir si le noeud est sélectionné, est étendu et est une feuille.
Swing propose une implémentation de cette interface dans la classe DefaultCellEditor qui permet de modifier la valeur du noeud sous la forme
d'une zone de texte, d'une case à cocher ou d'une liste déroulante grâce à trois constructeurs :
[Link](true);
String[] elements = { "Element 1", "Element 2", "Element 3", "Element 4"};
JComboBox jCombo = new JComboBox(elements);
DefaultTreeCellEditor editor = new DefaultTreeCellEditor(jTree,
new DefaultTreeCellRenderer(), new DefaultCellEditor(jCombo));
[Link](editor);
Par défaut, si la méthode setEditable(true) est utilisée alors tous les noeuds sont modifiables.
[Link] 26/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
Il est possible de définir les noeuds de l'arbre qui sont éditables en créant une classe fille de la classe JTree et en redéfinissant la méthode
isPathEditable().
Cette méthode est appelée avant chaque édition d'un noeud. Elle attend en paramètre un objet de type TreePath qui encapsule le chemin du
noeud à éditer.
Par défaut, elle renvoie le résultat de l'appel à la méthode isEditable(). Il est d'ailleurs important, lors de la redéfinition de la méthode
isPathEditable(), de tenir compte du résultat de la méthode isEditable() pour s'assurer que l'arbre est modifiable avant de vérifier si le noeud peut
être modifié.
Pour étendre un noeud et ainsi voir ses fils, l'utilisateur peut double cliquer sur l'icône ou sur le libellé du noeud. Il peut aussi cliquer sur le petit
commutateur à gauche de l'icône.
Enfin, il est possible d'utiliser le clavier pour naviguer dans l'arbre à l'aide des touches flèches haut et bas et des touches flèches droite et gauche
pour respectivement étendre ou refermer un noeud. Lors d'un appui sur la flèche gauche, si le noeud est déjà fermé alors c'est le noeud père qui
est sélectionné. De la même façon, lors d'un appui sur la flèche droite, si le noeud est étendu alors le premier noeud fils est sélectionné.
La touche HOME permet de sélectionner le noeud racine. La touche END permet de sélectionner le noeud qui est la dernière feuille du dernier
noeud. Les touches PAGEUP et PAGEDOWN permettent de parcourir rapidement les noeuds de l'arbre.
Depuis Java 2 version 1.3, la méthode setToggleClickCount() permet de préciser le nombre de clics nécessaires pour étendre ou refermer un
noeud.
La classe JTree propose plusieurs méthodes liées aux actions permettant d'étendre ou de refermer un noeud.
Méthode Rôle
public void expandRow (int row) Etendre le noeud dont l'index est fourni en paramètre
public void collapseRow(int row) Refermer le noeud dont l'index est fourni en paramètre
Les méthodes expandRow() et expandPath() ne permettent que d'étendre les noeuds fils directs du noeud sur lesquel elles sont appliquées. Pour
étendre les noeuds sous-jacents il est nécessaire d'écrire du code pour réaliser l'opération sur chaque noeud concerné de façon récursive.
Pour refermer tous les noeudsx et ne laisser que le noeud racine, il faut utiliser la méthode collapseRow() en lui passant 0 comme paramètre
puisque le noeud racine est toujours le premier noeud.
[Link](0);
La classe JTree propose deux méthodes pour forcer un noeud à être visible : scrollPathToVisible() et scrollRowToVisible(). Celles-ci ne peuvent
fonctionner que si le composant JTree est inclus dans un conteneur JScrollPane pour permettre au composant de scroller.
[Link](new TreeExpansionListener() {
public void treeExpanded(TreeExpansionEvent evt) {
[Link]("treeExpanded : path=" + [Link]());
[Link]([Link]());
}
[Link] 27/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
Pour déterminer le noeud sélectionné, il suffit d'utiliser la méthode getLastSelectedPathComponent() de la classe JTree et de caster la valeur
retournée dans le type du noeud, généralement de type DefaultMutableTreeNode. La méthode getObject() du noeud permet d'obtenir l'objet
associé au noeud. Si l'objet associé est simplement une chaîne de caractères ou si la valeur nécessaire est simplement le libellé du noeud, il suffit
d'utiliser la méthode toString().
Exemple ( code Java 1.1 ) : un bouton qui précise lors d'un clic le noeud sélectionné
...
private JButton getJButton() {
if (jButton == null) {
jButton = new JButton();
[Link](new [Link]() {
public void actionPerformed([Link] e) {
[Link]("actionPerformed()");
[Link]("Noeud sélectionné : "
+ [Link]().toString());
}
});
}
Il peut être nécessaire de parcourir tout ou partie des noeuds de l'arbre pour par exemple faire une recherche dans l'arborescence.
Si l'arbre est composé de noeuds de type DefaultMutableTreenode alors l'interface TreeNode propose plusieurs méthodes pour obtenir une
énumération des noeuds. L'ensemble, ou seulement une partie des données, peut être parcouru dans les deux sens et selon deux types de
présentation des valeurs.
Enumeration preorderEnumeration();
Enumeration postorderEnumeration();
Enumeration breadthFirstEnumeration();
Enumeration depthFirstEnumeration();
Exemple ( code Java 1.1 ) : un bouton qui précise lors d'un clic le noeud sélectionné
Enumeration e = ((DefaultMutableTreeNode)[Link]().getRoot()).preorderEnumeration();
while ([Link]()) {
[Link]([Link]() + " ");
}
Résultat :
[Link] 28/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
La méthode pathFromAncestorEnumeration(TreeNode ancestor) renvoie une énumération des noeuds entre le noeud sur lequel la méthode est
appelée et le noeud fourni en paramètre. Ainsi le noeud fourni en paramètre doit obligatoirement être un noeud fils direct ou indirect du noeud
sur lequel la méthode est appelée. Dans le cas contraire, une exception de type IllegalArgumentException est levée.
Il est possible d'attacher des listeners pour répondre aux événements liés à la sélection d'un élément ou l'extension ou la refermeture d'un noeud.
Durant son utilisation, le composant JTree ne gère pas directement les noeuds du modèle de données. La manipulation de ces noeuds se fait via
un index ou une instance de la classe TreePath.
L'utilisation de l'index est assez délicate car seul le noeud racine de l'arbre possède toujours le même index 0. Pour les autres noeuds, la valeur
de l'index dépend de l'état étendu/refermé de chaque noeud puisque seuls les noeuds affichés possèdent un index. Il est donc préférable d'utiliser
la classe TreePath.
Le modèle de données utilise des noeuds mais l'interface de l'arbre utilise une autre représentation sous la forme de la classe TreePath.
La classe DefaultMutableTreeNode est la représentation physique d'un noeud, la classe TreePath est la représentation logique. Elle encapsule le
chemin du noeud dans l'arborescence.
La méthode getPath() renvoie un tableau d'objets contenant chaque noeud qui compose le chemin encapsulé par la classe TreePath.
La méthode getPathComponent() permet de renvoyer le noeud dont l'index dans le chemin est fourni en paramètre. L'élément avec l'index 0 est
toujours le noeud racine de l'arbre.
La méthode getParentPath() renvoie une instance de la classe TreePath qui encapsule le chemin vers le noeud père du chemin encapsulé.
La méthode pathByAddingChild() renvoie une instance de la classe TreePath qui encapsule le chemin issu de l'ajout d'un noeud fils fourni en
paramètre.
La méthode idDescendant() renvoie un booléen qui précise si le chemin passé en paramètre est un descendant du chemin encapsulé.
La classe TreePath ne permet pas de gérer le contenu de chaque noeud mais uniquement son chemin dans l'arborescence. Pour accéder au noeud
à partir de son chemin, il faut utiliser la méthode getLastPathComponent(). Pour obtenir un noeud inclus dans le chemin, il faut utiliser la
getPathComponent() ou getPath(). Toutes ces méthodes renvoient un objet ou un tableau de type Object. Il est donc nécessaire de réaliser un
cast vers le type de noeud utilisé, généralement de type DefaultMutableTreeNode.
A partir d'un noeud de type DefaultMutableTreeNode, il est possible d'obtenir l'objet TreePath encapsulant le chemin du noeud. La méthode
getPath() permet d'obtenir un tableau d'objets de type TreeNode qu'il suffit de passer au constructeur de la classe TreePath.
La gestion de la sélection de noeud dans un composant JTree est déléguée à un modèle de sélection sous la forme d'une classe qui implémente
l'interface TreeSelectionModel. Par défaut, le composant JTree utilise une instance de la classe DefaultTreeSelectionModel.
Pour empêcher la sélection d'un noeud dans l'arbre, il faut supprimer son modèle de sélection en passant null à la méthode setSelectionModel().
La sélection d'un noeud peut être réalisée par l'utilisateur ou par l'application : le modèle de sélection s'assure que celle-ci est réalisée en
respectant le mode de sélection du modèle.
L'utilisateur peut utiliser la souris pour sélectionner un noeud ou appuyer sur la touche Espace sur le noeud courant pour le sélectionner. Il est
possible de sélectionner plusieurs noeuds en fonction du mode en maintenant la touche CTRL enfoncée. Avec la touche SHIFT, il est possible selon
le mode de sélectionner tous les noeuds entre un premier noeud sélectionné et le noeud courant.
[Link] 29/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
Le dernier noeud sélectionné peut être obtenu en utilisant les méthodes getLeadSelectionPath() ou getLeadSelectionRow().
Par défaut la sélection d'un noeud entraine l'extension des noeuds ascendants correspondant afin de les rendre visibles. Pour empêcher ce
comportement, il faut utiliser la méthode setExpandSelectedPath() en lui fournissant la valeur false en paramètre.
Les classes DefaultTreeSelectionModel et JTree possèdent plusieurs méthodes pour gérer la sélection de noeuds. Certaines de ces méthodes sont
communes à ces deux classes.
Méthode Rôle
TreePath getLeadSelectionPath() renvoie le dernier path ajouté à la sélection ou identifié comme tel
TreePath[] getSelectionPaths() Renvoie un tableau des chemins des noeuds inclus dans la sélection
int[] getSelectionRows() Renvoie un tableau des index des noeuds inclus dans la sélection
void removeSelectionRows(int[] rows) Enlève de la sélection les noeuds dont les index sont fournis en
paramètre
JTree uniquement
[Link] 30/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
void addSelectionRow(int row) Ajouter à la sélection le noeud dont l'index est fourni en paramètre
Définir la sélection avec les noeuds dont les index sont fournis en
void setSelectionInterval(int row0, int row1) paramètre
JTree uniquement
Définir la sélection avec les noeuds dont les chemins sont fournis
void setSelectionPaths (TreePath[] path)
en paramètre
Définir la sélection avec les noeuds dont les index sont fournis en
void setSelectionRows(int[] row) paramètre
JTree uniquement
Lors de la sélection d'un noeud, un événement de type TreeSelectionEvent est émis. Pour traiter cet événement, le composant doit enregistrer un
listener de type TreeSelectionListener.
[Link](new [Link]() {
public void valueChanged([Link] e) {
La classe TreeSelectionEvent possède plusieurs méthodes pour obtenir des informations sur la sélection.
Méthode Rôle
public TreePath[] getPaths() Renvoie un tableau des chemins des noeuds sélectionnés
[Link] 31/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
Un listener de type TreeSelectionListener est enregistré en utilisant la méthode addTreeSelectionListener() de la classe JTree.
[Link](new TreeSelectionListener() {
Un événement de type TreeSelectionEvent n'est émis que si un changement intervient dans la sélection : lors d'un clic sur un noeud, celui-ci est
sélectionné et un événement est émis. Lors d'un nouveau clic sur ce même noeud, le noeud est toujours sélectionné mais l'événement n'est pas
émis puisque la sélection n'est pas modifiée.
Dans un listener pour gérer les événements de la souris, il est possible d'utiliser la méthode getPathForLocation() pour déterminer le chemin d'un
noeud à partir des coordonnées de la souris qu'il faut lui fournir en paramètre.
La méthode getPathForLocation() renvoie null si l'utilisateur clique en dehors d'un noeud dans l'arbre.
[Link](new MouseAdapter() {
public void mouseClicked(MouseEvent evt) {
TreePath path =
[Link]([Link](), [Link]());
if (path != null) {
[Link]("path= " + [Link]());
}
}
});
Méthode Rôle
TreePath getPathForRow(int row) Renvoie le chemin du noeud dont l'index est fourni en paramètre
A chaque fois qu'un noeud est étendu ou refermé, un événement de type TreeExpansionEvent est émis. Il est possible de répondre à ces
événements en mettant en place un listener de type TreeExpansionListener.
[Link] 32/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
public void treeExpanded(TreeExpansionEvent event) public void treeCollapsed(TreeExpansionEvent event)
La classe TreeExpansionEvent possède une propriété source qui contient une référence sur le composant JTree à l'origine de l'événement et une
propriété path qui contient un objet de type TreePath encapsulant le chemin du noeud à l'origine de l'événement.
Les valeurs de ces deux propriétés peuvent être obtenues avec leurs getters respectifs : getSource() et getPath().
[Link](new TreeExpansionListener() {
});
Un seul événement est généré à chaque fois qu'un noeud est étendu ou refermé : il n'y a pas d'événements émis pour les éventuels noeuds fils
qui sont étendus ou refermés suite à l'action.
Il peut être utile de recevoir un événement avant qu'un noeud ne soit étendu ou refermé. Un listener de type TreeWillExpandListener() peut être
mis en place pour recevoir un événement de type TreeExpansionEvent lors d'une tentative pour étendre ou refermer un noeud.
Les deux méthodes peuvent lever une exception de type ExpandVetoException. Cette exception est levée si, pendant l'exécution d'une de ces
méthodes, des conditions sont remplies pour empêcher l'action demandée par l'utilisateur. Si l'exception n'est pas levée à la fin des traitements
de la méthode alors l'action est réalisée.
Exemple ( code Java 1.1 ) : empécher tous les noeuds étendus de se refermer
[Link](new TreeWillExpandListener() {
});
Le rendu du composant JTree dépend bien sûr dans un premier temps du look and feel utilisé mais il est aussi possible de personnaliser plus
finement le rendu des noeuds du composant.
Il est possible de préciser la façon dont les lignes reliant les noeuds sont rendues via une propriété client nommée lineStyle. Cette propriété peut
prendre trois valeurs :
Valeur Rôle
Angled Une ligne à angle droit relie chaque noeud fils à son noeud père
Horizontal Une simple ligne horizontale sépare les noeuds enfants du noeud racine
Pour préciser la valeur de la propriété que le composant doit utiliser, il faut utiliser la méthode putClientProperty() qui attend deux paramètres
sous forme de chaînes de caractères :
le nom de la propriété
sa valeur
[Link] 33/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
Il est possible de modifier l'apparence de la racine de l'arbre grâce à deux méthodes de la classe JTree : setRootVisible() et
setShowsRootHandles().
La méthode setRootVisible() permet de préciser avec son booléen en paramètre si la racine est affichée ou non.
Il est possible d'obtenir un contrôle total sur le rendu de chaque noeud en définissant un objet qui implémente l'interface TreeCellRenderer.
Attention, le rendu personnalisé est parfois dépendant du look & feel utilisé.
Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean
hasFocus)
Cette méthode envoie un composant qui va encapsuler le rendu du noeud. Le premier argument de type JTree encapsule le composant JTree lui-
même. L'argument de type Object encapsule le noeud dont le rendu doit être généré.
La méthode getCellRenderer() renvoie un objet qui encapsule le TreeCellRenderer. Il est nécessaire de réaliser un cast vers le type de cet objet.
Swing propose une classe de base DefaultTreeCellRenderer pour le rendu. Elle propose plusieurs méthodes pour permettre de définir le rendu.
Méthode Rôle
void setLeafIcon(Icon) Permet de définir l'icône associée au noeud lorsque celui-ci est une
feuille
[Link] 34/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
Un composant ne peut avoir qu'une seule instance de type TreeCellRenderer. Cette instance sera donc appelée pour définir le rendu de chaque
noeud.
Résultat :
Pour modifier les icônes utilisées par les différents éléments de l'arbre, il faut utiliser les méthodes setOpenIcon(), setClosedIcon() et
setLeafIcon().
Méthode Rôle
Pour simplement supprimer l'affichage de l'icône, il suffit de passer null à la méthode concernée.
Pour préciser une image, il faut créer une instance de la classe ImageIcon encapsulant l'image et la passer en paramètre de la méthode
concernée.
Il est aussi possible de définir une classe qui hérite de la classe DefaultTreeCellRenderer. Cette classe propose une implémentation par défaut de
l'interface TreeCellRenderer. Comme elle hérite de la classe JLabel, elle possède déjà de nombreuses méthodes pour assurer le rendu du noeud
sous la forme d'un composant de type étiquette.
import [Link];
import [Link];
import [Link];
import [Link];
[Link] 35/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
setBackgroundNonSelectionColor([Link]);
setBackgroundSelectionColor([Link]);
setTextSelectionColor([Link]);
setTextNonSelectionColor([Link]);
return this;
}
}
Une fois la classe de type DefaultTreeCellRenderer instanciée, il faut utiliser la méthode setCellRenderer() de la classe JTree pour indiquer à l'arbre
d'utiliser cette classe pour le rendu.
[Link](new MonTreeCellRenderer());
La création d'une classe fille de la classe DefaultTreeCellRenderer ne fonctionne correctement qu'avec les look and feel Metal et Windows car le
look and feel Motif définit son propre Renderer.
Le composant JTree ne propose pas de support pour les bulles d'aide en standard. Pour permettre à un composant JTree d'afficher une bulle
d'aide, il faut :
L'enregistrement du composant auprès du ToolTipManager se fait en utilisant la méthode registerComponent() sur l'instance partagée.
[Link]().registerComponent(jTree);
((JLabel)[Link]()).setToolTipText("Arborescence des données");
L'inconvénient de cette méthode est que la bulle d'aide est toujours la même quelque soit la position de la souris sur tous les noeuds du
composant. Pour assigner une bulle d'aide particulière à chaque noeud, il est nécessaire d'utiliser la méthode setToolTipText() dans la méthode
getTreeCellRendererComponent() d'une instance fille de la classe DefaultTreeCellRenderer
[Link](new DefaultTreeCellRenderer() {
return this;
}
});
[Link]().registerComponent(jTree);
Les menus sont mis en oeuvre dans Swing avec un ensemble de classe :
[Link] 36/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
JMenu hérite de la classe JMenuItem et non pas l'inverse car chaque JMenu contient un JMenuItem implicite qui encapsule le titre du menu.
La plupart des classes utilisées pour les menus implémentent l'interface MenuElement. Cette interface définit des méthodes pour la gestion des
actions standards de l'utilisateur. Ces actions sont gérées par la classe MenuSelectionManager.
Exemple :
package [Link];
import [Link].*;
import [Link].*;
import [Link].*;
public TestMenuSwing1() {
[Link](afficherMenuListener);
item = new JMenuItem("Sous menu 1 1");
[Link](item);
[Link](afficherMenuListener);
JMenu sousMenuDivers2 = new JMenu("Sous menu 1 2");
item = new JMenuItem("Sous menu 1 2 1");
[Link](item);
[Link](sousMenuDivers2);
[Link](sousMenuDiver1);
item = new JCheckBoxMenuItem("Validé");
[Link](item);
[Link] 37/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
[Link](afficherMenuListener);
[Link]();
ButtonGroup buttonGroup = new ButtonGroup();
item = new JRadioButtonMenuItem("Cas 1");
[Link](item);
[Link](afficherMenuListener);
[Link](item);
item = new JRadioButtonMenuItem("Cas 2");
[Link](item);
[Link](afficherMenuListener);
[Link](item);
[Link]();
[Link](item = new JMenuItem("Autre",
new ImageIcon("about_32.png")));
[Link](afficherMenuListener);
add(fichierMenu);
add(editerMenu);
add(diversMenu);
}
Résultat :
La classe JMenuBar utilise la classe DefaultSingleSelectionModel comme modèle de données : un seul de ces menus peut être activé à un instant
T.
Pour ajouter des menus à la barre de menus, il faut utiliser la méthode add() de la classe JMenuBar qui attend en paramètre l'instance du menu.
Pour ajouter la barre de menus à une fenêtre, il faut utiliser la méthode setJMenuBar() d'une instance des classes JFrame, JInternalFrame, JDialog
ou JApplet.
Comme la classe JMenuBar hérite de la classe JComponent, il est aussi possible d'instancier plusieurs JMenuBar et de les insérer dans un
gestionnaire de positionnement comme n'importe quel composant. Ceci permet aussi de placer le menu à sa guise.
Exemple :
...
public static void main(String s[]) {
JFrame frame = new JFrame("Test de menu");
[Link](JFrame.EXIT_ON_CLOSE);
TestMenuSwing1 menu = new TestMenuSwing1();
[Link]().add(menu, [Link]);
[Link](new Dimension(250, 200));
[Link]();
[Link](true);
}
...
Résultat :
[Link] 38/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
Swing n'impose pas d'avoir un unique menu par fenêtre : il est possible d'avoir plusieurs menus dans une même fenêtre.
Exemple :
...
public static void main(String s[]) {
JFrame frame = new JFrame("Test de menu");
[Link](JFrame.EXIT_ON_CLOSE);
[Link](new TestMenuSwing1());
TestMenuSwing1 menu = new TestMenuSwing1();
[Link]().add(menu, [Link]);
[Link](new Dimension(250, 200));
[Link]();
[Link](true);
}
...
Méthodes Rôle
Les éléments de menus peuvent être associés à deux types de raccourcis clavier :
les accelerators : ils sont hérités de JComponent : ce sont des touches (par exemple les touches de fonctions) ou des combinaisons de
touches avec les touches shift, Ctrl ou Alt qui sont affichées à la droite du libellé de l'élément du menu
les mnemonics : ils apparaissent sous la forme d'une lettre soulignée. Ils sont utilisables seulement sur certaines plate-formes (par exemple
en combinaison avec la touche Alt sous Windows).
[Link] 39/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
soit en utilisant la méthode setMnemonic()
Le mnemonic correspond à un caractère qui doit obligatoirement être contenu dans le libellé.
Un élément de menu peut contenir uniquement une image ou être composé d'un libellé et d'une image. Une image peut être associée à un
JMenuItem de deux façons :
La classe JPopupMenu encapsule un menu flottant qui n'est pas rattaché à une barre de menus mais à un composant.
Il est préférable d'ajouter un élément de type JMenuItem grâce à la méthode add() de la classe JPopupMenu mais on peut aussi ajouter
n'importe quel élément qui hérite de la classe Component en utilisant une surcharge de la méthode add().
Pour afficher un menu flottant, il faut ajouter un listener sur l'événement déclenchant et utiliser la méthode show() de la classe JPopupMenu.
Exemple :
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public TestMenuSwing2() {
[Link](new MouseAdapter() {
[Link] 40/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
private void afficherPopup(MouseEvent e) {
if ([Link]()) {
[Link](texte, [Link](), [Link]());
}
}
});
Le plus simple pour être multiplate-forme est de tester sur tous les événements de la souris ceux qui permettent l'affichage du menu flottant. Ce
test est réalisé grâce à la méthode isPopupTrigger() de la classe MouseEvent.
Un objet de type JPopupMenu peut émettre des événements de type PopupMenuEvent. Ceux-ci sont traités par un listener de type
PopupMenuListener qui définit trois méthodes :
Méthode Rôle
méthode appelée avant que l'affichage du menu déroulant ne soit
popupMenuCanceled()
annulé
popoupMenuWillBecomeInvisible() méthode appelée avant que le menu déroulant ne devienne invisible
méthode appelée avant que le menu déroulant ne devienne visible.
Cette méthode permet de personnaliser l'affichage des éléments du
popoupMenuWillBecomeVisible()
menu en fonction du contexte (exemple : rendre actif ou non
certains éléments du menu)
La classe JMenu encapsule un menu qui est attaché à un objet de type JMenuBar ou à un autre objet de type JMenu. Dans ce second cas, l'objet
est un sous menu.
Il est possible d'ajouter un élément sous la forme d'un objet de type JMenuItem, Component ou Action en utilisant la méthode add(). Chaque
élément du menu possède un index.
Propriété Rôle
popupMenu JPopupMenu qui encapsule les éléments du menu
propriété en lecture seule qui précise si le menu est attaché à un
topLevelMenu JMenuBar. La valeur false indique que le menu est un sous-menu
attaché à un autre menu
itemCount indique le nombre d'éléments du menu (incluant les séparateurs)
delay précise le temps en millisecondes avant l'affichage du menu
menuComponentCount indique le nombre de composants du menu
tearOff ne pas utiliser cette propriété qui lève une exception de type Error
La méthode getMenuComponent() permet d'obtenir le composant du menu dont l'index est fourni en paramètre. La méthode getItem() permet
d'obtenir le JMenuItem dont l'index est fourni en paramètre.
La méthode isMenuComponent() renvoie un booléen qui précise si le composant fourni en paramètre est inclus dans les éléments du menu.
Un événement de type MenuEvent est émis lorsque le titre du menu est cliqué. Un listener de type MenuListener permet de s'abonner à ces
événements. L'interface MenuListener définie trois méthodes qui possèdent un paramètre de type MenuEvent :
Méthodes Rôle
menuCanceled() invoquée lorsque le menu est effacé
menuDeselected() invoquée lorsque le titre du menu est désélectionné
menuSelected() invoquée lorsque le titre du menu est sélectionné
Elle possède de nombreux constructeurs qui permettent de préciser le texte, une icône et l'état de la case à cocher.
[Link] 41/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
La définition de ce groupe se fait en utilisant la classe ButtonGroup. C'est d'ailleurs cette classe qui propose la méthode getSelected() pour
connaître le bouton radio sélectionné dans le groupe.
Exemple :
...
[Link]();
ButtonGroup buttonGroup = new ButtonGroup();
item = new JRadioButtonMenuItem("Cas 1");
[Link](item);
[Link](afficherMenuListener);
[Link](item);
item = new JRadioButtonMenuItem("Cas 2");
[Link](item);
[Link](afficherMenuListener);
[Link](item);
[Link]();
...
Remarque : L'utilisation de cette classe ne se limite pas aux menus car elle peut aussi être utilisée comme un composant de l'interface.
Exemple :
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public TestMenuSwing3() {
super(true);
Résultat :
[Link] 42/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
La plus simple consiste à utiliser le composant JLabel qui est capable d'afficher du texte mais aussi une image
Exemple :
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Dans l'exemple ci-dessus, le fichier contenant l'image doit être à la racine des fichiers class : aucun chemin n'est précisé donc c'est le chemin
relatif au répertoire d'exécution de l'application qui est retenu. Il est possible de préciser un chemin absolu mais cela limite les possibilités de
déploiement de l'application.
C:\MonApp\src>javac com/jmdoudoux/test/[Link]
C:\MonApp\src>java [Link]
Il est possible de définir un composant personnalisé qui hérite de la classe JPanel qui va se charger d'afficher l'image.
Historiquement, c'est la classe [Link] qui peut être utilisée pour charger une image.
Exemple :
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
/**
* Composant qui affiche une image
*/
public class AfficheImage extends Panel {
[Link] 43/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
try {
MediaTracker mt = new MediaTracker(this);
[Link](image, 0);
[Link]();
} catch (Exception e) {
[Link]();
}
[Link](new Dimension([Link](this), image
.getHeight(this)));
}
L'inconvénient d'utiliser la classe Toolkit pour charger une image est que ce chargement se fait de façon asynchrone. Il faut alors utiliser une
instance de la classe MediaTracker pour patienter le temps du chargement de l'image et ainsi pouvoir déterminer sa taille pour la reporter sur la
taille du composant.
A partir de Java 1.4, il est aussi possible d'utiliser la classe [Link] pour simplifier le code qui charge l'image.
Exemple :
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
/**
* Composant qui affiche une image
*/
public class AfficheImage extends Panel {
try {
image = [Link](new File(nomFichier));
[Link](new Dimension([Link](),
[Link]()));
} catch (IOException ie) {
[Link]();
}
}
Exemple :
package [Link];
import [Link];
import [Link];
Malheureusement, ces deux solutions ne fonctionnent pas si l'application est packagée sous la forme d'une archive qui contient l'image car l'API
[Link] n'est pas capable de lire une ressource dans l'archive jar. Il faut utiliser le classloader pour charger l'image sous la forme d'une ressource.
L'avantage de cette solution c'est qu'elle fonctionne que l'application soit packagée ou non.
Exemple :
package [Link];
import [Link];
[Link] 44/45
01/11/2019 Cours Java et Eclipse de J.M. Doudoux
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
/**
* Composant qui affiche une image
*/
public class AfficheImage extends Panel {
Le fichier contenant l'image doit être accessible par le classloader dans le classpath, par exemple :
72 commentaires
Copyright (C) 1999-2016 Jean-Michel DOUDOUX. Vous pouvez copier, redistribuer et/ou modifier ce document selon les termes de la Licence
de Documentation Libre GNU, Version 1.1 ou toute autre version ultérieure publiée par la Free Software Foundation; les Sections Invariantes
étant constitués du chapitre Préambule, aucun Texte de Première de Couverture, et aucun Texte de Quatrième de Couverture. Une copie de
la licence est incluse dans la section GNU FreeDocumentation Licence. La version la plus récente de cette licence est disponible à l'adresse :
GNU Free Documentation Licence.
Responsables bénévoles de la rubrique Java : Mickael Baron - Robin56 - Contacter par email
[Link] 45/45