Arbres Et Tableaux

35
 Les arbres et les tableaux Chapitres traités Les arbres - JTree Nous avons décrit pratiquement l'ensemble des composants  swing  les plus utilisés pour concevoir des interfaces homme machine relativement sophistiq ués. Il nous reste à voir deux composants qui peuvent également s'avérer très utiles dans certaines situations. Le premier composant représenté par la classe  JTree  permet de visualiser une structure hiérarchique sous forme d'arbre. Naturellement, nous connaissons bien l'arbre proposé par les explorateurs qui visualise le système de fichier en séparant bien les répertoires, les sous- répertoires et les fichiers. Il existe un grand nombre de structures en arbres dans la vie de tous les jours, et nous verrons au cours de cette étude comment les mettre en oeuvre. De même, il existe dans  swing  un composant, JTable , très élaboré, qui affiche une grille bidimensionnelle d'objets. Là aussi, les tableaux sont très courant dans les interfaces utilisateur. De par leur nature, les tableaux sont compliqués, mais, peut-être plus que d'autres classes de  swing , le composant  JTable  prend en charge la plus grosse partie de cette complexité. Vous pourrez produire des tableaux parfaitement fonctionnels avec un comportement très riche, en écrivant uniquement quelques lignes de code. Les arbres -  JTree Tous les utilisateurs d'ordinateurs possédant un système de fichiers hiérarchiques ont déjà rencontré des arbres. En tant que programmeurs, il nous faut souvent afficher des structures hiérarchiq ues. La classe JTree  prend en charge l'organisation des arbres et le traitement des requêtes de l'utilisateur visant à ajouter et à supprimer des noeuds. Terminologie Avant de poursuivre, je pense qu'il est souhaitable de se mettre d'accord sur quelques éléments de terminologie : 1. Un arb re est comp osé de  noeuds . 2. Un noeud peut soit être une feuille, soit posséder des noeuds enfants. 3. Chaque noeud, à l'exception du noeud de départ ( la racine  ), possède un seul parent. 4. Un arbre possède un seul noeud de départ. 5. Des arbres peuvent être assemblés dans un groupe, chaque arbre possédant sa propre racine. Ce type de grou pe est appelé une  forêt . Modèl e MVC JTree  est l'un des composants les plus élaborés. Les arborescences conviennent parfaitement à la représentation hiérarchique d'informations, comme le contenu d'un disque dur ou l'organigramme d'une entreprise. Comme la plupart des autres composants  swing , le modèle de données est distinct de la représentation visuelle, et le composant  JTree  se doit de respecter cette architecture  Modèle-Vue-Contrôleur . Ainsi, un modèle de données hiérarchiques doit être fourni à l'arbre qui affiche alors ces données pour vous. Cela signifie que vous pouvez par exemple mettre à jour le modèle de données et être certain que le composant visuel sera correctement actualisé. JTree  est très puissant et complexe. En fait, il est si compliqué que les classes qui gèrent  JTree  possèdent leur propre paquetage,  javax.swing.tree . Néanmoins, si vous acceptez les options par défaut presque partout,  JTree  s'avère très simple à utiliser. Cré ation d'un ar bre Pour construire un JTree , et en respectant ce que nous venons d'évoquer, vous devez spécifier le modèle d'arbre dans le construsteur : TreeModel modèle = ... JTree ar bre = new JTree(modèle);

Transcript of Arbres Et Tableaux

Page 1: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 1/35

Les arbres et les tableaux 

Chapitres traités  Les arbres - JTree 

Nous avons décrit pratiquement l'ensemble des composants  swing  les plus utilisés pour concevoir des interfaces homme machine relativement sophistiqués. Il nous reste à voir deux composants qui peuvent également s'avérer très utiles dans certaines situations.

Le premier composant représenté par la classe JTree permet de visualiser une structure hiérarchique sous forme d'arbre. Naturellement,nous connaissons bien l'arbre proposé par les explorateurs qui visualise le système de fichier en séparant bien les répertoires, les sous- répertoires et les fichiers. Il existe un grand nombre de structures en arbres dans la vie de tous les jours, et nous verrons au cours de cette étude comment les mettre en oeuvre.

De même, il existe dans swing un composant, JTable , très élaboré, qui affiche une grille bidimensionnelle d'objets. Là aussi, les tableaux sont très courant dans les interfaces utilisateur. De par leur nature, les tableaux sont compliqués, mais, peut-être plus que d'autres classes de  swing , le composant JTable prend en charge la 

plus grosse partie de cette complexité. Vous pourrez produire des tableaux parfaitement fonctionnels avec un comportement très riche, en écrivant uniquement quelques lignes de code.

Les arbres - JTree 

Tous les utilisateurs d'ordinateurs possédant un système de fichiers hiérarchiques ont déjà rencontré des arbres. En tant que programmeurs, il nous faut souvent afficher 

des structures hiérarchiques. La classe JTree prend en charge l'organisation des arbres et le traitement des requêtes de l'utilisateur visant à ajouter et à supprimer des noeuds.

Terminologie

Avant de poursuivre, je pense qu'il est souhaitable de se mettre d'accord sur quelques éléments de terminologie : 

1. Un arbre est composé de noeuds .

2. Un noeud peut soit être une feuille, soit posséder des noeuds enfants.

3. Chaque noeud, à l'exception du noeud de départ ( la racine  ), possède un seul parent.

4. Un arbre possède un seul noeud de départ.

5. Des arbres peuvent être assemblés dans un groupe, chaque arbre possédant sa propre racine. Ce type de groupe est appelé une forêt .

Modèle MVC

JTree est l'un des composants les plus élaborés. Les arborescences conviennent parfaitement à la représentation hiérarchique d'informations, comme le contenu d'un disque dur ou l'organigramme d'une entreprise. Comme la plupart des autres composants  swing , le modèle de données est distinct de la représentation visuelle, et le 

composant JTree se doit de respecter cette architecture Modèle-Vue-Contrôleur .

Ainsi, un modèle de données hiérarchiques doit être fourni à l'arbre qui affiche alors ces données pour vous. Cela signifie que vous pouvez par exemple mettre à jour 

le modèle de données et être certain que le composant visuel sera correctement actualisé.

JTree est très puissant et complexe. En fait, il est si compliqué que les classes qui gèrent  JTree possèdent leur propre paquetage, javax.swing.tree . Néanmoins, si vous acceptez les options par défaut presque partout, JTree s'avère très simple à utiliser.

Création d'un arbre

Pour construire un JTree , et en respectant ce que nous venons d'évoquer, vous devez spécifier le modèle d'arbre dans le construsteur : 

TreeModel modèle = ...JTree arbre = new JTree(modèle);

Page 2: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 2/35

Il existe également des constructeurs qui permettent de créer des arbresà partir d'un ensemble d'éléments :

1. JTree( Object[] noeuds  ) 

2. JTree( Vector<?> noeuds  ) 

3. JTree( Hashtable<?, ?> noeuds  ) : les valeurs sont transformées en noeuds.

Ces constructeurs ne sont pas très utiles. Ils permettent surtout de générer des forêts d'arbres, chaque arbre possédant un seul noeud. Le troisième constructeur 

semble particulièrement inutile puisque les noeuds sont organisés selon l'ordre aléatoire fourni par les codes de hachage des clés.

Les noeuds et les modèles

Du coup, la question qui se pose, c'est comment obtenir un modèle d'arbre ?  Vous pouvez construire votre modèle en créant une classe qui implémente l'interface 

TreeModel . Nous verrons plus loin dans cette étude comment procéder. Le plus simple, consiste à prendre le modèle prédéfini par défaut dans la bibliothèque  swing , justement nommé DefaultTreeModel : 

TreeNode racine = ... ;TreeModel modèle = new DefaultTreeModel(racine);JTree arbre = new JTree(modèle);

Le modèle de données d'une arborescence est constitué de noeuds interconnectés. Un noeud possède un nom, en principe un parent et un certain nombre d'enfants 

(éventuellement aucun). Dans  swing , un noeud est représenté par l'interface  TreeNode . Les noeuds modifiables sont représentés cette fois-ci par l'interface 

MutableTreeNode  qui hérite en fait de TreeNode . Là aussi, nous pouvons créer des classes de noeud qui implémentent ces interfaces, toutefois il existe une implémentaiton concrète de l'interface MutableTreeNode qui se nomme DefaultMutableTreeNode .

TreeNode racine = new DefaultMutableTreeNode( "Noeud racine"  );TreeModel modèle = new DefaultTreeModel( racine );

 JTree arbre = new JTree( modèle );

Structure d'un noeud d'arbre mutable

Un noeud d'arbre mutable par défaut renferme un objet, et plus précisément un objet de l'utilisateur. L'arbre peut transformer les objets de l'utilisateur contenus dans 

chaque noeud. A moins que vous ne spécifiiez une méthode de transformation, l'arbre se contente d'afficher une chaîne résultant de la méthode  toString() .

Dans l'exemple précédent, nous nous servons de chaînes comme objets de l'utilisateur. En pratique, vous remplirez probablement des arbres avec des objets d'utilisateur plus importants. Par exemple, pour afficher un arbre de répertoire, il convient de le remplir avec des objets  File .

Vous pouvez spécifier le type des objets d'utilisateur dans le constructeur, mais vous pouvez également le définir par la suite grâce à la méthode  setUserObject()  : 

DefaultMutableTreeNode noeud = new  DefaultMutableTreeNode ( "Un noeud"  );noeud. setUserObject ( "Un autre noeud"  );

Mise en place de la hiérarchie

Ensuite, il faut établir les relations hiérarchies entre les parents et les enfants pour chaque noeud.

1. Commencez par le noeud racine, et utilisez la méthode add() pour ajouter des enfants : 

DefaultMutableTreeNode racine = new  DefaultMutableTreeNode ( "Images"  );

DefaultMutableTreeNode jpeg = new  DefaultMutableTreeNode ( "JPEG"  );DefaultMutableTreeNode gif = new  DefaultMutableTreeNode ( "GIF"  );DefaultMutableTreeNode png = new  DefaultMutableTreeNode ( "PNG"  );racine.add (jpeg);racine.add (gif);racine.add (png);

2. Vous devez relier tous les noeuds de cette manière. Construisez ensuite un DefaultTreeModel  avec le noeud racine. Pour terminer, construisez un JTree avec le 

modèle de l'arbre : 

TreeModel  modèle = new DefaultTreeModel( racine ); JTree arbre = new JTree( modèle );

3. Plus simplement, il suffit de passer le noeud racine au constructeur JTree() . L'arbre construit alors automatiquement un modèle d'arbre par défaut : 

 JTree arbre = new JTree( racine );

Première mise en application

Afin d'illustrer notre première approche sur la gestion d'une structure arborescente, je vous propose de mettre en oeuvre une petite application qui permet de visualiser une 

image à partir d'un répertoire prédéterminé. Le choix de l'image s'effectue à partir d'un arbre situé sur la partie gauche. Cet arbre recense les images par type d'extension : JPEG , GIF et PNG .

codage correspondant 

package arbres;

import  javax.swing.*;import  java.awt.*;import  java.awt.image.BufferedImage;import  java.io.*;import  javax.imageio.ImageIO;import  javax.swing.event.*;import  javax.swing.tree.DefaultMutableTreeNode ;

public class Arbres extends JFrame implements TreeSelectionListener {

  private JTree arbre;  private Vue vue = new Vue();  private String répertoire = "C:/Photos/"; 

public Arbres() {  super("Images");  construireArbre ();  add(new JScrollPane(arbre), BorderLayout.WEST);  add(vue);  setSize(540, 300);  setDefaultCloseOperation (EXIT_ON_CLOSE);  setVisible(true);

Page 3: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 3/35

  public static void main(String[] args) { new Arbres(); }

  private void construireArbre() {  File fichiers = new File(répertoire);  DefaultMutableTreeNode  racine = new DefaultMutableTreeNode ("Images");  DefaultMutableTreeNode   jpeg = new DefaultMutableTreeNode ("JPEG");  DefaultMutableTreeNode  gif = new DefaultMutableTreeNode ("GIF");  DefaultMutableTreeNode  png = new DefaultMutableTreeNode ("PNG");  racine.add( jpeg);  racine.add(gif );  racine.add(png);  for (String nom : fichiers.list()) {  if (nom.endsWith(".gif")) gif .add(new DefaultMutableTreeNode (nom));  else if (nom.endsWith(".jpeg") || nom.endsWith(".jpg")) jpeg.add(new DefaultMutableTreeNode (nom));  else if (nom.endsWith(".png")) png.add(new DefaultMutableTreeNode (nom));

}arbre = new JTree(racine);arbre.setPreferredSize (new Dimension(180, 1000));arbre.addTreeSelectionListener (this);

}

  private class Vue extends JComponent {  private BufferedImage photo;  private double ratio;

@Override  protected void paintComponent(Graphics g) {  if (photo!=null) g.drawImage(photo, 0, 0, getWidth(), (int)(getWidth()/ratio), null);

public void setPhoto(File fichier) {  try {  photo = ImageIO.read(fichier);  ratio = (double)photo.getWidth() / photo.getHeight();  repaint();

}catch (IOException ex) { setTitle("Impossible de lire le fichier");}

}}

  public void valueChanged(TreeSelectionEvent  e) {  if (arbre.getSelectionPath()!=null) {  String nom = arbre.getSelectionPath().getLastPathComponent ().toString();  vue.setPhoto(new File("C:/Photos/"+nom));

}}

}

Lorsque vous exécutez ce programme, seuls le noeud racine ( Images  ) et ses enfants sont visibles ( JPEG, GIF et PNG  ). Cliquez sur les poignées pour ouvrir les 

arbres de niveau inférieur. Le segment dépassant des poignées se trouve sur la droite lorsque le sous-répertoire est caché, et il pointe vers le bas lorsque le sous- 

répertoire est affiché.

Il semble que ces segments représentent des poignées de porte. Il faut appuyer sur la poignée pour ouvrir le sous-répertoire.§ 

Changer l'apparence de l'arbre 

Par défaut l'arbre affiche des lignes entre les parents et les enfants, ce qui permet de bien vérifier rapidement l'appartenance des différents éléments (le style par défaut est Angled  ).

Il est possible d'enlever ces lignes de liaisons. Utilisez pour cela la méthode putClientProperty() de la classe JTree . Positionnez alors la propriété JTree.lineStyle à None : 

arbre.putClientProperty("JTree.lineStyle", "None");

A l'inverse, pour vous assurez que les lignes sont bien affichées, utilisez : 

arbre. putClientProperty( "JTree.lineStyle"  , "Angled"  );

Page 4: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 4/35

Un autre style appelé  Horizontal  permet d'afficher l'arbre avec des lignes horizontales séparant uniquement les enfants du noeud 

racine.

arbre. putClientProperty( "JTree.lineStyle"  , "Horizontal"  );

 

Par défaut, il n'existe aucune poignée pour cacher la racine d'un arbre. Si vous le désirez, vous pouvez en ajouter une avec la 

méthode setShowsRootHandles()  : 

arbre.setShowsRootHandles(true);

Inversement, la racine peut être entièrement cachée. Cela peut être utile si vous souhaitez afficher une  forêt , c'est-à-dire un 

ensemble d'arbres possédant chacun leur propre racine. Vous devez cependant regrouper tous les arbres de la forêt avec une 

seule racine commune. Vous devez alors cacher cette racine au moyen de la méthode  setRootVisible()  : 

arbre.setRootVisible(false);

 

Il est possible de combiner ces deux dernières méthodes afin que tous les arbres de la forêt disposent de poignées pour faciliter le développement des feuilles : 

arbre.setShowsRootHandles(true);arbre.setRootVisible(false);

 

Passons maintenant de la racine aux feuilles de l'arbre. Notez que les feuilles possèdent une icône différente de celle des autres noeuds. Lorsque l'arbre est affiché, chaque noeud est représenté par une icône. Il existe en fait trois sortes d'icônes : 

1. Les icônes de feuilles.

2. Les icônes de noeuds intermédiaires ouverts.

3. Les icônes de noeuds intermédiaires fermés.

Pour des raisons de simplicité, nous appelerons les deux dernières icônes, des icônes de répertoire.

§ 

L'afficheur de noeud doit savoir quelle icône utiliser pour chaque noeud. Par défaut, cette décision est prise de la façon 

suivante : si la méthode  isLeaf()  d'un noeud renvoie  true , l'icône de feuille est utilisée. Sinon, une icône de répertoire est 

utilisée.

Page 5: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 5/35

La méthode  isLeaf()  de la classe DefaultMutableTreeNode  renvoie true si le noeud ne possède aucun enfant. Par conséquent, les 

noeuds possédant des enfants sont associés à des icônes de répertoire, et les noeuds sans enfant sont associés à des icônes de feuille.

Parfois, cette technique n'est pas toujours appropriée. Supposons que nous ajoutions un noeud "Autre"  dans notre exemple de visualisation d'images, qui permet de recenser les autres formats d'images, mais que le répertoire en question 

ne possède pas d'éléments. Il convient cependant d'éviter d'affecter une icône de feuille à ce noeud puisque seuls les 

fichiers images correspondent à des feuilles.

La classe JTree ne possède aucune information lui permettant de déterminer si un noeud doit être considéré comme une 

feuille ou comme un répertoire. Elle le demande donc au modèle de l'arbre. Si un noeud sans enfant n'est pas toujours 

interprété au plan conceptuel comme une feuille, vous pouvez demander au modèle d'utiliser différents critères pour vérifier qu'un noeud est bien une feuille, en interrogeant la propriété AllowsChildren  d'un noeud. Régler cette propriété au moyen 

de la méthode setAllowsChildren()  : 

1. Pour les noeuds correspondant à des feuilles qui ne devraient donc pas avoir d'enfant : 

noeud. setAllowsChildren( false );

2. Pour les noeuds qui possèdent des enfants : 

noeud. setAllowsChildren( true );

Il est possible également d'utiliser le constructeur de la classe  DefaultMutableTreeNode qui dispose de deux paramètres, le deuxième attend une valeur booléenne spécifiant si le noeud va posséder des enfants ou pas : 

1. Pour les noeuds correspondant à des feuilles qui ne devraient donc pas avoir d'enfant : 

DefaultMutableTreeNode noeud = new DefaultMutableTreeNode ( "Noeud"  , false);

2. Pour les noeuds qui possèdent des enfants : 

DefaultMutableTreeNode noeud = new DefaultMutableTreeNode ( "Noeud"  , true);

Ensuite, il faut indiquer au modèle qu'il doit examiner la propriété  AllowsChildren  d'un noeud pour savoir s'il doit être 

affiché avec une icône de feuille ou non. La méthode  setAskAllowsChildren()  de la classe  DefaultTreeModel  permet de 

définir ce comportement : 

TreeModel  modèle = new DefaultTreeModel( racine );modèle. setAskAllowsChildren( true ) ;

Ici aussi, vous pouvez également prévoir cette fonctionnalité directement à partir du constructeur du modèle, en proposant 

la valeur true sur le deuxième argument : 

TreeModel  modèle = new DefaultTreeModel( racine , true );

A partir de ces critères de décision, les noeuds susceptibles d'avoir des enfants sont associés à des icônes de répertoire, et les

autres à des icônes de feuilles.

Sinon, si vous construisez un arbre en fournissant un noeud racine (sans modèle), vous pouvez spécifier ce comportement 

également directement dans le constructeur de la classe JTree : 

 JTree arbre = new JTree( racine , true );

modification du source correspondant à l'apparence ci-contre 

private void construireArbre() {  File fichiers = new File(répertoire);  DefaultMutableTreeNode racine = new DefaultMutableTreeNode ("Images");  DefaultMutableTreeNode jpeg = new DefaultMutableTreeNode ("JPEG", true);  DefaultMutableTreeNode gif = new DefaultMutableTreeNode ("GIF", true);  DefaultMutableTreeNode png = new DefaultMutableTreeNode ("PNG", true);  DefaultMutableTreeNode autre = new DefaultMutableTreeNode ("Autre", true);

racine.add(jpeg);racine.add(gif);racine.add(png);racine.add(autre);

  for (String nom : fichiers.list()) {  if (nom.endsWith(".gif")) gif.add(new DefaultMutableTreeNode (nom, false));  else if (nom.endsWith(".jpeg") || nom.endsWith(".jpg")) jpeg.add(new DefaultMutableTreeNode (nom, false));  else if (nom.endsWith(".png")) png.add(new DefaultMutableTreeNode (nom, false));  else autre.add(new DefaultMutableTreeNode (nom, false));

}arbre = new JTree(racine, true);

  arbre.setPreferredSize (new Dimension(180, 1000));  arbre.addTreeSelectionListener (this);}

Identifier les noeuds d'un arbre 

Une fois que l'arbre est constitué, notamment de façon automatique, il peut être intéressant de retrouver certains noeuds suivant les critères désirés. La classe 

DefaultMutableTreeNode dispose d'un grand nombre de méthodes qui vons nous aider pour résoudre ce problème. Deux démarches sont utilisées : 

1. La première consiste à énumérer l'ensemble des noeuds, quelque soit la profondeur, à partir d'un noeud précis.

2. La deuxième nous permet de récupérer un noeud en particulier suivant sa qualité : une feuille, un enfant, un parent, etc.

Page 6: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 6/35

Enumération des noeuds

Il arrive parfois que vous deviez trouver un noeud dans un arbre, en partant de la racine et en passant en revue tous les enfants jusqu'à ce que vous ayez trouvé le noeud.

La classe DefaultMutableTreeNode possède plusieurs méthodes pratiques pour parcourir les noeuds d'un arbre.

Les méthodes breadthFirstEnumeration()  et depthFirstEnumeration()  renvoient des objets d'énumération dont la méthode nextElement()  parcourt tous les enfants du 

noeud courant, en utilisant soit une approche horizontale, soit une approche verticale.

1. L'approche horizontale est la plus simple à visualiser. L'arbre est parcouru par niveaux, en commençant par la racine, suivie de tous ses enfants, puis de tous ses petits-enfants, etc.

2. Pour visualiser une approche verticale , imaginez qu'un rat soit emprisonné dans un labyrinthe en forme d'arbre. Il descend l'arbre jusqu'à ce qu'il trouve une 

feuille, puis il remonte d'un niveau et parcourt la prochaine branche, etc.

Cette dernière approche est aussi appelée une traversée postérieure en informatique, parce que la recherche commence par les enfants avant d'arriver aux parents.

La méthode  postOrderTraversal()  est donc équivalente à la méthode  depthFirstTraversal() . Pour que la bibliothèque soit complète, il existe aussi une méthode preOrderTraversal() qui propose également une recherche verticale qui passe en revue les parents avant les enfants.

Voici un exemple typique d'utilisation :

Enumeration recherche = racine.breadthFirstEnumeration ();while (recherche.hasMoreElements()) {  DefaultMutableTreeNode  noeud = (DefaultMutableTreeNode ) recherche.nextElement();

...}

Il existe une méthode pathFromAncestorEnumeration() qui trouve un chemin entre un ancêtre et un noeud spécifié, puis parcourt tous les noeuds se trouvant sur ce chemin. Cette méthode est assez simple, en fait elle se contente d'appeler la méthode  getParent()  jusqu'à ce que l'ancêtre spécifié soit trouvé, puis elle affiche 

ensuite en sens inverse le chemin parcouru.

Enumeration recherche = feuille.breadthFirstEnumeration ( racine );

Pour terminer la méthode children() renvoit une énumération des enfants (immédiats : premier niveau sans les petits enfants) d'un noeud : 

Enumeration enfants = racine.children();

Recherche d'un noeud en particulier

Il existe ensuite des méthodes de la classe DefaultMutableTreeNode que nous allons recenser, qui vont nous permettre de naviguer dans l'arborescence à la recherche d'un 

noeud en particulier : 

TreeNode getChildAfter( TreeNode enfant  ) : renvoie le noeud enfant suivant celui proposé en argument de la méthode.

TreeNode getChildBefore( TreeNode enfant  ) : renvoie le noeud enfant précédent celui proposé en argument de la méthode.

int getChildCount()  : renvoie le nombre de noeuds enfant.

int getDepth()  : renvoie la profondeur d'imbrication des noeuds allant de ce noeud jusqu'à la feuille la plus éloignée.

TreeNode getFirstChild()  : renvoie le premier noeud enfant.

DefaultMutableTreeNode getFirstLeaf() : Recherche et renvoie la première feuille présente dans ce noeud ancêtre.

TreeNode getLastChild() : renvoie le dernier noeud enfant.DefaultMutableTreeNode getLastLeaf()  : Recherche et renvoie la dernière feuille présente dans ce noeud ancêtre.

int getLeafCount() : renvoie le nombre de feuilles présentes à partir de ce noeud ancêtre.

int getLevel() : renvoie le niveau d'imbrication de ce noeud à partir de la racine.

DefaultMutableTreeNode getNextLeaf() : renvoie la prochaine feuille à partir de celle-ci ou null si c'est la dernière.

DefaultMutableTreeNode getNextNode()  : renvoie le noeud suivant.

DefaultMutableTreeNode getNextSibling()  : renvoie le noeud frère suivant.

Page 7: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 7/35

TreeNode getParent() : renvoie le noeud parent ou null si le la demande est faite par le noeud racine.

TreeNode[] getPath() : renvoie le chemin complet allant de la racine au noeud concerné.

DefaultMutableTreeNode getPreviousLeaf() : renvoie la précédente feuille à partir de celle-ci ou null si c'est la première.

DefaultMutableTreeNode getPreviousNode() : renvoie le noeud précédent.

DefaultMutableTreeNode getPreviousSibling()  : renvoie le noeud frère précédent.

TreeNode getRoot() : renvoie le noeud racine.

TreeNode getSharedAncestor( DefaultMutableTreeNode noeud  ) : renvoie le plus proche noeud commun.

int getSiblingCount() : renvoie le nombre de frères.

Object  getUserObject()  : renvoie l'objet utilisateur, c'est-à-dire l'objet qui est placé dans le noeud. C'est très souvent un texte, mais cela peut être n'importe quel 

type d'objet.

boolean isLeaf() : indique si ce noeud ne dispose pas d'enfant qui dans ce cas là se nomme une feuille.

boolean isNodeAncestor( TreeNode autreNoeud  ) : indique si le noeud passé en argument est un noeud ancêtre de celui-ci.

boolean i sNodeChild( TreeNode noeud  ) : indique si le noeud passé en argument est un enfant de celui-ci.

boolean isNodeDescendant( DefaultMutableTreeNode autreNoeud  ) : indique si le noeud passé en argument est un descendant de celui-ci.

boolean isNodeRelated( DefaultMutableTreeNode noeud  ) : indique si le noeud passé en argument fait parti du même arbre.

boolean isNodeSibling( TreeNode autreNoeud  ) : indique si le noeud passé en argument est un frère de celui-ci.

boolean isRoot()  : s'agit-il du noeud racine ? 

String toString() : renvoie l'intitulé du noeud sous forme de chaîne de caractères.

void add( MutableTreeNode nouvelEnfant  ) : Enlève ce neud enfant de l'arborescence précédente et le rajoute à la fin des enfants de ce noeud-ci.

void remove( MutableTreeNode noeud  ) : Enlève le noeud spécifié en argument faisant parti de la descendance du noeud en cours.

void removeAllChildren()  : Enlève l'ensemble des noeuds enfants faisant partis du noeud en cours.

void removeFromParent() : Enlève le noeud actuel ainsi que ses descendant de l'arborescence actuelle. Le noeud actuel ne dispose alors plus de parent et devient 

donc noeud racine.

void setAllowsChildren( boolean fils  ) : détermine si le noeud courant doit posséder des enfants ou pas.

void setParent( MutableTreeNode nouveauParent  ) : propose un nouveau parent au noeud en cours.

void  setUserObject( Object  objetUtilisateur  )  : propose un nouvel objet utilisateur au noeud en cours, c'est-à-dire l'objet qui est placé dans le noeud. C'est très 

souvent un texte, mais cela peut être n'importe quel type d'objet.

Mise en pratique de ces différentes recherches

Nous allons reprendre l'application précédente sur laquelle nous allons faire quelque petites modifications. Nous allons en effet restructurer l'arborescence de l'arbre des fichiers. Le noeud racine s'appelle cette fois-ci  "Fichiers" . Ce noeud racine comporte deux autres noeuds : le premier intitulé  "Images" et le second "Autre" . Cette fois-ci, la 

mise en place des feuilles de l'arbre, correspondant aux fichiers présents dans le répertoire choisi, s'effectue automatiquement suivant le nom des noeuds fils proposés à 

partir du noeud "Images" . S'il reste des fichiers, ceux-ci sont automatiquement placés dans le noeud "Autre" .

codage correspondant 

package arbres;

import  javax.swing.*;import  java.awt.*;import  java.awt.image.BufferedImage;import  java.io.*;import  java.util.*;import  javax.imageio.ImageIO;import  javax.swing.event.*;import  javax.swing.tree.*;

public class Arbres extends JFrame implements TreeSelectionListener {  private JTree arbre;  private DefaultMutableTreeNode racine;  private DefaultMutableTreeNode images;  private DefaultMutableTreeNode autre;  private Vue vue = new Vue();  private String répertoire = "C:/Photos/";

  public Arbres() {

  super("Images");  construireArbre ();  add(new JScrollPane(arbre), BorderLayout.WEST);  add(vue);  setSize(540, 300);  setDefaultCloseOperation (EXIT_ON_CLOSE);  setVisible(true);

public static void main(String[] args) { new Arbres(); }

  private void construireArbre() {racine = new DefaultMutableTreeNode ("Fichiers", true);images = new DefaultMutableTreeNode ("Images", true);

Page 8: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 8/35

  DefaultMutableTreeNode jpeg = new DefaultMutableTreeNode ("JPG", true);DefaultMutableTreeNode gif = new DefaultMutableTreeNode ("GIF", true);

  DefaultMutableTreeNode png = new DefaultMutableTreeNode ("PNG", true);autre = new DefaultMutableTreeNode ("Autre", true);racine.add(images);racine.add(autre);images.add(jpeg);images.add(gif);images.add(png);

  arbre = new JTree(racine, true);  arbre.setPreferredSize (new Dimension(180, 1000));  arbre.addTreeSelectionListener (this);  ajouterFichiers();

private void ajouterFichiers() {  File fichiers = new File(répertoire);  ArrayList<String> liste = new ArrayList<String>();  for (String nom : fichiers.list()) liste.add(nom);

Enumeration recherche = images.children();  while (recherche.hasMoreElements()) {  DefaultMutableTreeNode noeud = (DefaultMutableTreeNode ) recherche.nextElement();

for (String nom : liste) {  String extension = nom.split("\\.")[1];  if (extension.equalsIgnoreCase(noeud.toString()))

noeud.add(new DefaultMutableTreeNode (nom, false));}

}for (DefaultMutableTreeNode noeud = images.getFirstLeaf (); noeud !=null; noeud = noeud.getNextLeaf ())

liste.remove(noeud.toString());  for (String nom : liste) autre.add(new DefaultMutableTreeNode (nom, false));

}

private class Vue extends JComponent {  private BufferedImage photo;  private double ratio;

@Override  protected void paintComponent(Graphics g) {

  if (photo!=null) g.drawImage(photo, 0, 0, getWidth(), (int)(getWidth()/ratio), null);} 

public void setPhoto(File fichier) {  try {

photo = ImageIO.read(fichier);  ratio = (double)photo.getWidth() / photo.getHeight();  repaint();

}catch (IOException ex) { setTitle("Impossible de lire le fichier");}

}}

  public void valueChanged(TreeSelectionEvent  e) {  if (arbre.getSelectionPath()!=null) {  String nom = arbre.getSelectionPath().getLastPathComponent ().toString();

vue.setPhoto(new File("C:/Photos/"+nom));}

}}

 

Modifier des arbres et leur structure 

Dans ce chapitre, nous allons apprendre à modifier un arbre en  "temps réel" . Il est par exemple possible d'ajouter un nouveau noeud par rapport à un autre noeud de référence. Ce nouveau noeud peut être considéré comme un fils du noeud de référence ou comme un frère ( sibling  ). Le noeud de référence peut aussi être supprimé à tout 

moment.

Pour implémenter ce comportement, vous devrez identifier le noeud sélectionné. La classe JTree possède une technique étonnante pour identifier les noeuds d'un 

arbre. Elle ne gère pas les noeuds de l'arbre, mais les chemins des objets, appelés chemins de l'arbre. Un chemin d'arbre commence à la racine et correspond à une séquence de noeuds enfant.

Vous vous demandez peut-être pourquoi la classe JTree a besoin du chemin complet. Ne peut-elle pas se contenter de récupérer un  TreeNode et d'appeler en boucle 

sa méthode  getParent() ? En fait, la classe  JTree ne connaît pas du tout l'interface TreeNode . Cette interface n'est en effet jamais utilisée par l 'interface TreeModel .Elle ne sert qu'à l'implémentation de  DefaultTreeModel . Vous pouvez posséder d'autres modèles d'arbres dans lesquels les noeuds n'implémentent pas du tout 

l'interface  TreeNode . Si vous avez recours à un modèle d'arbre qui gère d'autres types d'objets, ces derniers peuvent ne pas avoir de méthodes  getParent()  et 

getChild() . Ils doivent cependant posséder des connexions entre eux. Cette tâche revient au modèle d'arbre. La classe JTree elle-même n'a aucune idée de la nature de leurs connexions. Pour cette raison, la classe JTree doit toujours travailler avec des chemins complets.

Retrouver la sélection

La classe TreePath gère une séquence de références d' Object (et pas de TreeNode  ). Un certain nombre de méthode de JTree renvoient des objets TreePath . Lorsque vous 

possédez un chemin d'arbre, il vous suffit en général de connaître le noeud final, que vous pouvez récupérer grâce à la méthode getLastPathComponent() . Par exemple,pour trouver quel noeud est couramment sélectionné dans un arbre, vous pouvez utiliser la méthode getSelectionPath() de la classe JTree . Vous obtiendrez en retour un 

objet TreePath , d'où vous déduirez le noeud sélectionné : 

TreePath chemin = arbre.getSelectionPath();DefaultMutableTreeNode  noeud = (DefaultMutableTreeNode ) chemin.getLastPathComponent();

En fait, comme cette requête est très fréquente, il existe une méthode pratique qui vous fournit immédiatement le noeud sélectionné : 

DefaultMutableTreeNode  noeud = ( DefaultMutableTreeNode ) arbre. getLastSelectedPathComponent();

Cette méthode n'est pas appelée  getSelectedNode() parce que l'arbre ne sait pas qu'il renferme des noeuds. Seul le modèle d'arbre gère les chemins des objets.

Les chemins d'arbre sont l'une des deux techniques utilisées par la classe  JTree  pour décrire les noeuds. Il existe d'autres méthodes JTree  qui acceptent ou renvoient un indice entier, une position de ligne. Une position de ligne est simplement un numéro de ligne (commençant par 0) correspondant au noeud spécifié dans 

l'arbre affiché. Seuls les noeuds visibles posèdent un numéro de ligne, et le numéro de ligne d'un noeud change si les noeuds qui le précèdent sont cachés, affichés 

ou modifiés. C'est pourquoi, il vaut mieux éviter de travailler avec des positions de ligne. Toutes les méthodes de  JTree  qui se servent de lignes possèdent un équivalent utilisant des chemins d'arbre.

Page 9: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 9/35

Modifications sur un noeud

Une fois que vous avez trouvé le noeud sélectionné, vous pouvez le modifier. Cependant, ne vous contentez pas d'ajouter des enfants à un noeud : 

noeud.add(nouveauNoeud); // non

Si vous modifiez la structure des noeuds, vous modifiez le modèle, mais l'affichage associé n'est pas mis à jour. Vous pouvez envoyer une notification par vous- 

même, mais si vous utilisez la méthode insertNodeInto()  de la classe DefaultTreeModel , vous refaites le travail de la classe du modèle. Par exemple, l'appel suivant 

ajoute un noeud et le déclare comme étant le dernier noeud du noeud sélectionné, et met à jour l'affichage de l'arbre : 

modèle.inserNodeInto( nouveauNoeud  , noeud  , noeud .getChildCount());

L'appel similaire à removeNodeFromParent() supprime un noeud et met à jour l'affichage : 

modèle.removeNodeFromParent( noeud  );

Si vous conservez la structure des noeuds, mais que vous modiffiez un objet utilisateur, vous devrez appeler la méthode  nodeChanged() : 

modèle.nodeChanged( noeudChangé );

La classe  DefaultTreeModel  possède une méthode  reload()  qui recharge le modèle entier. Cependant, évitez d'appeler cette méthode uniquement pour 

mettre à jour votre arbre lorsque vous avez apporté des modifications. Lorsqu'un arbre est généré à nouveau, tous les noeuds situés après les enfants de la racine sont cachés. Cela peut être extrêmement déconcertant pour vos utilisateurs, s'ils doivent ouvrir à nouveau leur arbre après chaque modification.

La classe DefaultTreeModel  possède également une méthode  reload()  qui permet de ne recharger que les descendants d'un noeud particulier spécifié en 

argument de la méthode.

Pour apporter des modifications sur les noeuds d'un arbre, vous remarquez que vous êtes obligé de passer systématiquement par le modèle de l'arbre.

Soit, vous construisez ce modèle dès le départ, au moyen de la classe DefaultTreeModel  que vous passez ensuite en argument du constructeur de l'arbre 

JTree . Ou bien, vous le récupérez à partir de l'arbre au moyen de la méthode getModel() de la calsse JTree .

Gestion de l'affichage et construction de chemins d'arbre

Lorsque l'affichage est mis à jour à cause d'une modification de la structure des noeuds, les enfants ajoutés ne sont pas automatiquement affichés. En particulier, si l'un 

des utilisateurs ajoutait un nouvel enfant à un noeud dont les enfants sont actuellement cachés, le nouvel enfant le serait aussi. Cela ne fournit à l'utilisateur aucune information sur le fonctionnement de la commande qu'il vient d'effectuer. Dans ce cas, il convient d'ouvrir tous les noeuds parent pour que le noeud qui vient d'être ajouté 

soit visible. Vous pouvez vous servir de la méthode  makeVisible() de la classe JTree dans ce but. La méthode makeVisible()  attend un chemin d'arbre pointant sur le noeud 

qu'elle doit rendre visible.

Par conséquent, vous serez amené à construire un chemin d'arbre à partir de la racine et allant jusqu'au nouveau noeud. Pour obtenir un chemin d'arbre, il faut commencer par appeler la méthode getPathToRoot()  de la classe DefaultTreeModel . Elle renvoie un tableau  TreeNode[] contenant tous les noeuds situés entre un 

noeud et la racine. Vous pouvez alors passer ce tableau à un constructeur TreePath : 

TreeNode[] noeuds = modèle. getPathToRoot( nouveauNoeud  );TreePath chemin = new TreePath( noeuds );arbre.makeVisible( chemin );

Il est assez étrange que la classe DefaultTreeModel  fasse semblant d'ignorer la classe TreePath , même si son travail est de communiquer avec un JTree . La 

classe JTree se sert beaucoup de chemins d'arbre, alors qu'elle n'utilise jamais de tableaux d'objets de noeuds.

Mais supposons maintenant que votre arbre fasse partie d'un panneau d'affichage déroulant. Après l'expansion des noeuds de l'arbre, le nouveau noeud risque une 

nouvelle fois de ne pas être visible parce qu'il peut se trouver en dehors de la zone visible du panneau. Pour résoudre ce problème, appelez la méthode scrollPathToVisible()  au lieu d'appeler la méthode  makeVisible() . Cet appel ouvre tous les noeuds du chemin et demande au panneau déroulant de se positionner sur 

le noeud situé à la fin du chemin : 

arbre. scrollPathToVisible( chemin );

Edition d'un noeud

Par défaut, les noeuds d'un arbre peuvent être modifiés. Cependant si vous validez la méthode  setEditable() , l'utilisateur peut modifier un noeud en double-cliquant 

simplement dessus, en modifiant la chaîne, puis en appuyant sur la touche "Entrée" : 

arbre.setEditable(true);

Le système invoque alors l'éditeur de cellule par défaut, qui est implémenté par la classe  DefaultCellEditor . Il est possible d'installer d'autres éditeurs de cellules,mais je préfére reporter notre étude sur les éditeurs de cellules à la section concernant les tableaux, avec lesquels les éditeurs de cellules sont plus couramment 

utilisés.

Mise en oeuvre sur l'application précédente

A titre d'exemple, je vous propose de reprendre l'application précédente et de faire en sorte de pouvoir rajouter ou supprimer des noeuds dans l'arborescence des fichiers.

Au départ, seul le noeud "JPG" existe dans le répertoire "Images" . Il est possible d'intégrer un nouveau noeud seulement si c'est un fils du noeud "Images" . Il existe deux 

possibilités pour cela, soit à partir du noeud "Image"  lui-même, soit à partir d'un fils déjà créé, comme le noeud  "JPG" . Dans ce cas là, nous rajoutons un noeud frère,comme cela est visualisé dans la capture ci-dessous. Les fichiers se déplacent alors en conséquence suivant les extensions proposées. A tout moment, il est également 

possible de supprimer un noeud particulier de l'arborescence.

Page 10: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 10/35

codage correspondant 

package arbres;

import  javax.swing.*;import  java.awt.*;import  java.awt.event.*;import  java.awt.image.BufferedImage;import  java.io.*;import  java.util.*;import  java.util.ArrayList;

import  javax.imageio.ImageIO;import  javax.swing.event.*;import  javax.swing.tree.*;

public class Arbres extends JFrame implements TreeSelectionListener {  private JTree arbre;  private DefaultTreeModel  modèle;  private DefaultMutableTreeNode racine;  private DefaultMutableTreeNode images;  private DefaultMutableTreeNode autre;  private Vue vue = new Vue();  private String répertoire = "C:/Photos/";  private JToolBar barre = new JToolBar();  private JTextField saisie = new JTextField("Nouveau répertoire"); 

public Arbres() {  super("Images");  construireArbre ();  barre.add(new AbstractAction("Ajouter Frère") {  public void actionPerformed(ActionEvent e) {

  DefaultMutableTreeNode sélection = (DefaultMutableTreeNode ) arbre.getLastSelectedPathComponent ();  if (images.isNodeChild(sélection)) {  System.out.println("Noeud enfant");  DefaultMutableTreeNode noeud = new DefaultMutableTreeNode (saisie.getText(), true);  modèle.insertNodeInto(noeud, images, 0);  DefaultMutableTreeNode  recherche = autre.getFirstLeaf ();  while (recherche!=null) {  DefaultMutableTreeNode suivant = recherche.getNextLeaf ();  String extension = recherche.toString().split("\\.")[1];  if (extension.equalsIgnoreCase(noeud.toString()))

modèle.insertNodeInto(recherche, noeud, 0);  recherche = suivant;

}modèle.reload(autre);

}}

});  barre.add(new AbstractAction("Ajouter Fils") {  public void actionPerformed(ActionEvent e) {  DefaultMutableTreeNode sélection = (DefaultMutableTreeNode ) arbre.getLastSelectedPathComponent ();

  if (sélection.equals(images)) {  DefaultMutableTreeNode noeud = new DefaultMutableTreeNode (saisie.getText(), true);  modèle.insertNodeInto(noeud, images, 0);  DefaultMutableTreeNode  recherche = autre.getFirstLeaf ();  while (recherche!=null) {  DefaultMutableTreeNode suivant = recherche.getNextLeaf ();  String extension = recherche.toString().split("\\.")[1];  if (extension.equalsIgnoreCase(noeud.toString()))

modèle.insertNodeInto(recherche, noeud, 0);  recherche = suivant;

}modèle.reload(autre);

}}

});  barre.add(new AbstractAction("Supprimer") {  public void actionPerformed(ActionEvent e) {  DefaultMutableTreeNode sélection = (DefaultMutableTreeNode ) arbre.getLastSelectedPathComponent ();  DefaultMutableTreeNode noeud = sélection.getFirstLeaf ();  while (noeud!=null) {

  DefaultMutableTreeNode suivant = noeud.getNextLeaf ();  modèle.insertNodeInto(noeud, autre, 0);noeud = suivant;

}  modèle.removeNodeFromParent(sélection);

}});

  barre.add(saisie);  add(barre, BorderLayout.NORTH);  add(new JScrollPane(arbre), BorderLayout.WEST);  add(vue);  setSize(540, 330);  setDefaultCloseOperation (EXIT_ON_CLOSE);  setVisible(true);

Page 11: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 11/35

public static void main(String[] args) { new Arbres(); }

  private void construireArbre() {racine = new DefaultMutableTreeNode ("Fichiers", true);images = new DefaultMutableTreeNode ("Images", true);

  DefaultMutableTreeNode jpeg = new DefaultMutableTreeNode ("JPG", true);autre = new DefaultMutableTreeNode ("Autre", true);racine.add(images);racine.add(autre);images.add(jpeg);

  modèle = new DefaultTreeModel (racine, true);  arbre = new JTree(modèle);  arbre.setPreferredSize (new Dimension(180, 1000));  arbre.addTreeSelectionListener (this);  arbre.setEditable(true);  ajouterFichiers();

}

  private void ajouterFichiers() {  File fichiers = new File(répertoire);  ArrayList<String> liste = new ArrayList<String>();  for (String nom : fichiers.list()) liste.add(nom);

Enumeration recherche = images.children();  while (recherche.hasMoreElements()) {  DefaultMutableTreeNode noeud = (DefaultMutableTreeNode ) recherche.nextElement();

for (String nom : liste) {  String extension = nom.split("\\.")[1];  if (extension.equalsIgnoreCase(noeud.toString()))

noeud.add(new DefaultMutableTreeNode (nom, false));}

}

for (DefaultMutableTreeNode noeud = images.getFirstLeaf (); noeud !=null; noeud = noeud.getNextLeaf ())liste.remove(noeud.toString());

  for (String nom : liste) autre.add(new DefaultMutableTreeNode (nom, false));}

  private class Vue extends JComponent {  private BufferedImage photo;  private double ratio;

@Override  protected void paintComponent(Graphics g) {  if (photo!=null) g.drawImage(photo, 0, 0, getWidth(), (int)(getWidth()/ratio), null);

public void setPhoto(File fichier) {  try {

photo = ImageIO.read(fichier);ratio = (double)photo.getWidth() / photo.getHeight();

  repaint();}catch (IOException ex) { setTitle("Impossible de lire le fichier");}

}}

  public void valueChanged(TreeSelectionEvent  e) {  if (arbre.getSelectionPath()!=null) {  String nom = arbre.getSelectionPath().getLastPathComponent ().toString();

vue.setPhoto(new File("C:/Photos/"+nom));}

}}

 

Affichage des noeuds personnalisés 

Dans vos applications, vous serez souvent amené à modifier la manière dont un composant d'un arbre représente les noeuds. La modification la plus courante est 

naturellement la possibilité de choisir plusieurs icônes pour les noeuds et pour les feuilles. Les autres changements peuvent être en rapport avec la police utilisée ou 

l'affichage d'images sur chaque noeuds.

Toutes ces modifications sont possibles si vous prenez la peine d'installer un nouvel  afficheur de cellules d'arbre dans votre arbre. Par défaut, la classe JTree se sert d'objets de type DefaultTreeCellRenderer pour afficher chaque noeud. La classe  DefaultTreeCellRenderer  étend la classe JLabel . Une étiquette contient l'icône d'un 

noeud et le nom de ce noeud.

L'afficheur de cellules n'affiche pas les poignées permettant de savoir si un noeud est ouvert ou fermé. Ces poignées font partie de l'aspect général de l'arbre, et il 

est recommandé de ne pas les changer.

Personnalisation

Vous pouvez personnaliser l'affichage de trois manières différentes : 

1. Vous pouvez modifier les icônes, la police et la couleur de fond utilisées par un objet  DefaultTreeCellRenderer  déjà présent dans l'arbre. Dans ce cas là, ces 

paramètres sont utilisés pour tous les noeuds d'un arbre .

2. Vous pouvez installer un nouvel afficheur qui étend la classe DefaultTreeCellRenderer et modifier les icônes, les polices et la couleur de fond de chaque noeud.

3. Vous pouvez installer un afficheur qui implémente l'interface TreeCellRenderer , pour afficher une nouvelle image pour chaque noeud.

Personnaliser les icônes des noeuds (même apparence pour tous les noeuds)

Si vous désirez modifier les icônes des répertoires (ouvert ou fermé) ainsi que celle des feuilles tout en gardant la même apparence sur l'ensemble de l'arbre, il suffit : 

1. soit de récupérer l'objet DefaultTreeCellRenderer de l'arbre à partir de la méthode  getCellRenderer() de la classe JTree ,

2. soit de créer un nouvel objet DefaultTreeCellRenderer et de le proposer ensuite à l'arbre au travers de la méthode  setCellRenderer() .

Quelque soit la solution retenue, la classe DefaultTreeCellRenderer possède, en plus de celles récupérées par héritage issue de la classe  JLabel , des méthodes spécifiques à la gestion d'affichage des noeuds : 

Méthodes spécifiques à la classe DefaultTreeCellRenderer 

Color getBackgroundNonSelectionColor()

void setBackgroundNonSelectionColor(Color nouvelleCouleur )

Page 12: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 12/35

Retourne ou spécifie la couleur de fond du noeud lorsque ce dernier n'est pas sélectionné.

Color getBackgroundSelectionColor()

void setBackgroundSelectionColor(Color nouvelleCouleur)

Retourne ou spécifie la couleur de fond du noeud lorsque ce dernier est sélectionné.

Color getBorderSelectionColor()

void setBorderSelectionColor(Color nouvelleCouleur)

Retourne ou spécifie la couleur de bordure du noeud lorsque ce dernier est sélectionné.

Icon getClosedIcon()

void setClosedIcon(Icon nouvelleIcône)

Retourne ou spécifie l'icône qui représente un noeud fermé (répertoire fermé). Ce noeud n'est pas une feuille et possède des enfants.

Icon getDefaultClosedIcon()

Retourne l'icône par défaut représentant un noeud fermé. Ce noeud n'est pas une feuille.

Icon getDefaultLeafIcon()

Retourne l'icône par défaut représentant une feuille.

Icon getDefaultOpenIcon()

Retourne l'icône par défaut représenant un noeud ouvert (répertoire ouvert). Ce noeud n'est pas une feuille et possède des enfants.

Icon getLeafIcon()

void setLeafIcon(Icon nouvelleIcône)

Retourne ou spécifie l'icône qui représente une feuille.

Icon getOpenIcon()

void setOpenIcon(Icon nouvelleIcône)

Retourne ou spécifie l'icône qui représente un noeud ouvert (répertoire ouvert). Ce noeud n'est pas une feuille et possède des enfants.

Color getTextNonSelectionColor()void setTextNonSelectionColor(Color nouvelleCouleur)

Retourne ou spécifie la couleur du libellé du noeud lorsque ce dernier n'est pas sélectionné.

Color getTextSelectionColor()

void setTextSelectionColor(Color nouvelleCouleur)

Retourne ou spécifie la couleur du libellé du noeud lorsque ce dernier est sélectionné.

Component getTreeCellRendererComponent(JTree arbre, Object valeur, boolean sélection, boolean ouvert, boolean feuille, int ligne, boolean focus)

Méthode à redéfinir lorsque nous souhaitons proposer un rendu personnalisé pour chaque noeud. Cette méthode est déclarée dans l'interfaceTreeCellRenderer.

Exemple de mise en oeuvre en récupérant l'objet DefaultTreeCellRenderer de l'arbre JTree 

  private void construireArbre() {racine = new DefaultMutableTreeNode ("Fichiers", true);images = new DefaultMutableTreeNode ("Images", true);

  DefaultMutableTreeNode jpeg = new DefaultMutableTreeNode ("JPG", true);DefaultMutableTreeNode gif = new DefaultMutableTreeNode ("GIF", true);

  DefaultMutableTreeNode png = new DefaultMutableTreeNode ("PNG", true);autre = new DefaultMutableTreeNode ("Autre", true);racine.add(images);racine.add(autre);images.add(jpeg);images.add(gif);images.add(png);

  arbre = new JTree(racine, true);  arbre.setPreferredSize (new Dimension(200, 1000));  arbre.addTreeSelectionListener (this);  arbre.setShowsRootHandles (true);  arbre.setRootVisible(false);

  DefaultTreeCellRenderer rendu = (DefaultTreeCellRenderer) arbre.getCellRenderer();  rendu.setLeafIcon(new ImageIcon("feuille.gif"));  rendu.setClosedIcon(new ImageIcon("répertoireFermé.gif"));  rendu.setOpenIcon(new ImageIcon("répertoireOuvert.gif"));  ajouterFichiers();

}

Autre exemple en créant un nouvel objet DefaultTreeCellRenderer qui est ensuite proposé à l'arbre JTree 

Page 13: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 13/35

  private void construireArbre() {racine = new DefaultMutableTreeNode ("Fichiers", true);images = new DefaultMutableTreeNode ("Images", true);

  DefaultMutableTreeNode jpeg = new DefaultMutableTreeNode ("JPG", true);DefaultMutableTreeNode gif = new DefaultMutableTreeNode ("GIF", true);

  DefaultMutableTreeNode png = new DefaultMutableTreeNode ("PNG", true);autre = new DefaultMutableTreeNode ("Autre", true);racine.add(images);racine.add(autre);images.add(jpeg);images.add(gif);images.add(png);

  arbre = new JTree(racine, true);  arbre.setPreferredSize (new Dimension(200, 1000));  arbre.addTreeSelectionListener (this);  arbre.setShowsRootHandles (true);  arbre.setRootVisible(false);  DefaultTreeCellRenderer rendu = new DefaultTreeCellRenderer ();  rendu.setLeafIcon(new ImageIcon("feuille.gif"));  rendu.setClosedIcon(new ImageIcon("répertoireFermé.gif"));  rendu.setOpenIcon(new ImageIcon("répertoireOuvert.gif"));

arbre.setCellRenderer(rendu);  ajouterFichiers();

}

Avec la première méthode, la première fois que l'arbre s'affiche, la dimension des icônes est celle prévue par le système par défaut. Nous avons donc un petit aléa 

qui est vite résorbé dès que nous ouvrons un répertoire quelconque. Toutefois, pour éviter cet aléa, il est préférable d'utiliser la deuxième méthode.

Il n'est généralement pas souhaitable de modifier la police ou la couleur de fond d'un arbre entier, parce que cette tâche revient plutôt au look-and-feel choisi.

§ 

Personnaliser l'apparence de chaque noeud

Pour modifier l'apparence de certains noeuds, vous devez installer un afficheur de cellules d'arbre. Cet afficheur de cellule doit impérativement implémenter l'interface TreeCellRenderer qui possède une seule méthode getTreeCellRendererComponent(). Nous avons déjà rencontré cette méthode dans la classe DefaultTreeCellRenderer , et 

c'est normal puisque cette dernière implémente justement cette interface.

Pour personnaliser l'apparence de chaque noeud en particulier, il suffit finalement de créer un nouvel afficheur de cellule qui hérite de la classe 

DefaultTreeCellRenderer et nous devons redéfinir la méthode getTreeCellRendererComponent() pour que cette dernière soit adaptée au rendu personnalisé.

Interface TreeCellRenderer 

Component getTreeCellRendererComponent(JTree arbre, Object valeur, boolean sélection, boolean ouvert, boolean feuille, int ligne, boolean focus)

Méthode à redéfinir lorsque nous souhaitons proposer un rendu personnalisé pour chaque noeud.

Attention  : le paramètre  valeur  de la méthode  getTreeCellRendererComponent()  est l'objet noeud, et non l'objet de l'utilisateur ! Rappelez-vous que l'objet de 

l'utilisateur est une caractéristique de DefaultMutableTreeNode , et qu'un  JTree peut contenir des noeuds de n'importe quel type. Si votre arbre se sert de noeuds 

DefaultMutableTreeNode , vous devez traiter l'objet de l'utilisateur dans une seconde étape. Pour récupérer l 'objet utilisateur, passez par la méthode getUserObject() de la classe DefaultMutableTreeNode .

Attention  :  DefaultTreeCellRenderer  se sert d'un seul objet d'étiquette pour tous les noeuds, et il ne modifie le texte de l'étiquette que d'un seul noeud. Si, par 

exemple, vous souhaitez modifier la police d'un noeud particulier, vous devez lui redonner sa valeur par défaut lorsque la méthode est appelée à nouveau.

Autrement, tous les noeuds suivant seront affichés avec la nouvelle police.

 

Afficher une petite vignette sur la feuille correspondant à la photo à visualiser 

A titre d'exemple, je vous propose de reprendre l'application précédente et de personnaliser l'icône de chaque feuille. Il est effectivement plus judicieux de proposer une petite vignette correspondant à la photo à visualiser.

Page 14: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 14/35

package arbres;

import  javax.swing.*;import  java.awt.*;import  java.awt.image.BufferedImage;import  java.io.*;import  java.util.*;import  javax.imageio.ImageIO;import  javax.swing.event.*;

import  javax.swing.tree.*;

public class Arbres extends JFrame implements TreeSelectionListener {  private JTree arbre;  private DefaultMutableTreeNode racine;  private DefaultMutableTreeNode images;  private DefaultMutableTreeNode autre;  private Vue vue = new Vue();  private String répertoire = "C:/Photos/";

  public Arbres() {  super("Images");  construireArbre ();  add(new JScrollPane(arbre), BorderLayout.WEST);  add(vue);  setSize(540, 300);  setDefaultCloseOperation (EXIT_ON_CLOSE);  setVisible(true);

public static void main(String[] args) { new Arbres(); }

  private void construireArbre() {racine = new DefaultMutableTreeNode ("Fichiers", true);images = new DefaultMutableTreeNode ("Images", true);

  DefaultMutableTreeNode jpeg = new DefaultMutableTreeNode ("JPG", true);DefaultMutableTreeNode gif = new DefaultMutableTreeNode ("GIF", true);

  DefaultMutableTreeNode png = new DefaultMutableTreeNode ("PNG", true);autre = new DefaultMutableTreeNode ("Autre", true);racine.add(images);racine.add(autre);images.add(jpeg);images.add(gif);images.add(png);

  arbre = new JTree(racine, true);  arbre.setPreferredSize (new Dimension(200, 1000));  arbre.addTreeSelectionListener (this);  arbre.setShowsRootHandles (true);  arbre.setRootVisible(false);  arbre.setCellRenderer(new RenduArbre());  ajouterFichiers();

private void ajouterFichiers() {  File fichiers = new File(répertoire);

ArrayList<Vignette> vignettes = new ArrayList<Vignette>();  for (File fichier : fichiers.listFiles()) vignettes.add(new Vignette(fichier));  Enumeration recherche = images.children();  while (recherche.hasMoreElements()) {  DefaultMutableTreeNode  noeud = (DefaultMutableTreeNode ) recherche.nextElement();

for (Vignette vignette : vignettes)  if (vignette.mêmeExtension(noeud))

noeud.add(new DefaultMutableTreeNode (vignette, false));}for (DefaultMutableTreeNode  noeud = images.getFirstLeaf (); noeud !=null; noeud = noeud.getNextLeaf ())

vignettes.remove(noeud.getUserObject());  for (Vignette vignette : vignettes) autre.add(new DefaultMutableTreeNode (vignette, false));

public void valueChanged(TreeSelectionEvent  e) {  if (arbre.getSelectionPath()!=null) {

  DefaultMutableTreeNode noeud = (DefaultMutableTreeNode ) arbre.getLastSelectedPathComponent ();  Vignette vignette = (Vignette) noeud.getUserObject();

vue.setPhoto(vignette.getFichier());}

private class Vue extends JComponent {  private Image photo;  private double ratio;

@Override  protected void paintComponent(Graphics g) {  if (photo!=null) g.drawImage(photo, 0, 0, getWidth(), (int)(getWidth()/ratio), null);

Page 15: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 15/35

public void setPhoto(File fichier) {photo = new ImageIcon(fichier.getPath()).getImage();ratio = (double)photo.getWidth(null) / photo.getHeight(null);

  repaint();}

}

  private class RenduArbre extends DefaultTreeCellRenderer{  public RenduArbre() {  setClosedIcon(new ImageIcon("répertoireFermé.gif"));  setOpenIcon(new ImageIcon("répertoireOuvert.gif"));

setBorder(BorderFactory.createEmptyBorder(1, 0, 1, 0));}

 @Override

  public Component getTreeCellRendererComponent (JTree arbre, Object n, boolean sélection,boolean ouvert, boolean feuille, int ligne, boolean focus) {

  super.getTreeCellRendererComponent (arbre, n, sélection, ouvert, feuille, ligne, focus);  DefaultMutableTreeNode noeud = (DefaultMutableTreeNode ) n;  if (feuille) {  Vignette vignette = (Vignette) noeud.getUserObject();  setIcon(vignette.getIcon());

}  return this;

}}

 private class Vignette extends JLabel {

  private final int largeur = 50;  private File fichier;  private String libellé;  private String extension; 

public Vignette(File fichier) {  this.fichier = fichier;  String[] découpage = fichier.getName().split("\\.");

libellé = découpage[0];

extension = découpage[1];  Image image = new ImageIcon(fichier.getPath()).getImage().getScaledInstance(largeur, -1, Image.SCALE_DEFAULT);  setIcon(new ImageIcon(image));

}

  boolean mêmeExtension(DefaultMutableTreeNode noeud) {  return extension.equalsIgnoreCase(noeud.toString());

}

  public File getFichier() { return fichier; } 

@Override  public String toString() { return libellé; }

}}

 

Ecouter les événements des arbres 

Le plus fréquemment, et c'est justement le cas dans notre application, un composant arbre est couplé à un autre composant. Lorsque l'utilisateur sélectionne des noeuds 

de l'arbre, certaines informations s'affichent dans une autre fenêtre. Dans notre application, lorsque l'utilisateur sélectionne une feuille, la photo correspondante s'affiche 

sur la partie centrale de la fenêtre.

Une arborescence déclenche plusieurs sortent d'événements qui sont gérés par des interfaces écouteurs d'événement distincts. Nous pouvons déterminer : 

1. Le moment où les noeuds ont été ouverts et refermés, implémenté par l'interface TreeExpansionListener .

2. Celui où ils sont sur le point d'être ouverts ou refermé (suite à un clic de l'utilisateur), implémenté par l'interface TreeWillExpandListener .

3. Et celui où les sélections ont cours, implémenté par l'interface TreeSelectionListener .

écouteur TreeExpansionListener 

void treeCollapsed(TreeExpansionEvent événement)

Appelé lorsque un noeud se referme.

void treeExpanded(TreeExpansionEvent événement)

Page 16: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 16/35

Appelé lorsque noeud s'ouvre.

écouteur TreeWillExpandListener 

void treeWillCollapse(TreeExpansionEvent événement)

Appelé lorsque un noeud est sur le point de se refermer.

void treeWillExpande( TreeExpansionEvent événement)

Appelé lorsque noeud est sur le point de s'ouvrir.

écouteur TreeSelectionListener 

void valueChanged(TreeSelectionEvent événement)

Appelé lorsque l'utilisateur sélection ou désélectionne des noeuds de l'arbre.

Réagir à la sélection d'un noeud

Bien entendu, comme pour notre application, le cas le plus fréquent est la prise en compte d'une sélection, qui permet de réagir en conséquence en lançant la visualisation de l'image souhaitée. Pour obtenir ce comportement, il suffit donc d'installer  un écouteur de sélection de l'arbre . Cet écouteur doit implémenter l'interface 

TreeSelectionListener , une interface possédant une seule méthode : 

void valueChanged(TreeSelectionEvent événement);

Cette méthode est appelée lorsque l'utilisateur sélectionne ou désélectionne des noeuds de l'arbre. L'écouteur est ajouté à l'arbre de manière classique :

arbre.addTreeSelectionListener(écouteur);

Choisir le mode de sélection

Vous pouvez autoriser l'utilisateur à sélectionner un seul noeud, une zone continue de noeuds ou un ensemble arbitraire et potentiellement discontinu de noeuds. La classe 

JTree se sert de l'interface TreeSelectionModel  pour gérer la sélection des noeuds. Vous devrez retrouver le modèle, au moyen de la méthode  getSelectionModel()  de la classe JTree , pour définir ensuite l'état de sélection, à l'aide de la méthode  setSelectionMode()  de l'interface TreeSelectionModel , avec l'une des valeurs suivantes : 

1. TreeSelectionModel.SINGLE_TREE_SELECTION ,

2. TreeSelectionModel.CONTIGUOUS_TREE_SELECTION ,

3. TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION .

Le mode de sélection discontinu est le mode par défaut. Pour permettre uniquement la sélection d'un seul noeud : 

int mode = TreeSelectionModel.SINGLE_TREE_SELECTION  ;arbre. getSelectionModel().setSelectionMode( écouteur  );

Lorsque vous aurez défini un mode de sélection, vous n'aurez plus à vous préocuper du modèle de sélection de l'arbre.

Récupération de la sélection courante

Pour récupérer la sélection courante, il faut interroger l'arbre avec la méthode  getSelectionPaths()  : 

TreePath[] sélections = arbre.getSelectionPaths();

Si vous limitez l'utilisateur à une seule sélection, vous pouvez avoir recours à la méthode pratique  getSelectionPath() , qui renvoie le premier chemin sélectionné, ou null si aucun chemin n'a été sélectionné. Vous obtiendrez en retour un objet  TreePath , d'où vous en déduirez le noeud sélectionné. Effectivement, lorsque vous 

possédez un chemin d'arbre, il vous suffit en général de connaître le noeud final, que vous pouvez récupérer grâce à la méthode  getLastPathComponent() 

TreePath chemin = arbre. getSelectionPath();DefaultMutableTreeNode  noeud = ( DefaultMutableTreeNode ) chemin. getLastPathComponent();

En fait, comme cette requête est très fréquente, il existe une méthode pratique qui vous fournit immédiatement le noeud sélectionné : 

DefaultMutableTreeNode  noeud = ( DefaultMutableTreeNode ) arbre. getLastSelectedPathComponent();

Cette méthode n'est pas appelée  getSelectedNode()  parce que l'arbre ne sait pas qu'il renferme des noeuds. Seul le modèle d'arbre gère les 

chemins des objets.§ 

Attention : la classe TreeSelectionEvent  possède une méthode getPaths()  qui renvoie un tableau d'objets TreePath , mais ce tableau décrit les modifications de la sélection, et non la sélection courante.

 

Mise en oeuvre de la gestion de sélection unique d'une feuille de l'arbre 

...public class Arbres extends JFrame implements TreeSelectionListener {...  private void construireArbre() {...  arbre = new JTree(racine, true);  arbre.setPreferredSize (new Dimension(200, 1000));  arbre.addTreeSelectionListener (this);  arbre.setShowsRootHandles (true);  arbre.setRootVisible(false);  arbre.setCellRenderer(new RenduArbre());

  arbre.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);  ajouterFichiers();

}...

public void valueChanged(TreeSelectionEvent  e) {  if (arbre.getSelectionPath()!=null) {  DefaultMutableTreeNode noeud = (DefaultMutableTreeNode ) arbre.getLastSelectedPathComponent ();  Vignette vignette = (Vignette) noeud.getUserObject();

vue.setPhoto(vignette.getFichier());}

}

Page 17: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 17/35

...}

 

Les tableaux 

Les tableaux présentent les informations sous forme de lignes et de colonnes ordonnées. Ils conviennent particulièrement à la représentation de schémas financiers ou de 

données de base de données relationnelle. Comme les arborescences, les tableaux de  Swing sont extrêmement puissants et personnalisables. Si on se cantonne à leurs 

options par défaut, ils s'avèrent eu outre très simples à utiliser.

Les tableaux sont représentés par le composant  JTable  qui affiche une grille bidirectionnelle d'objets. Naturellement, les tableaux sont très courants dans les interfaces utilisateurs. De par leur nature, les tableaux sont compliqués, mais, peut-être plus que pour d'autres classes de Swing , le composant JTable  prend en 

charge la plus grosse partie de cette complexité. Vous pourrez ainsi produire des tableaux parfaitement fonstionnels avec un comportement riche, en écrivant 

uniquement quelques lignes de code. Mais vous pouvez bien sûr écrire un code plus complet et personnaliser l'affichage et le comportement de vos applications.

Gestion de la table par modèlesLe composant JTable comporte un certain nombre de modèles internes qui permettent de respecter l'architecture  MVC : 

1. Comme pour les composants d'un arbre, un JTable n'enregistrement pas ses propres données, mais il les obtient à partir d'un modèle de tableau issu de l'interface 

TableModel . Tous les élements important de cette classe JTable sont construits dans le paquetage  javax.swing.table  comme l'est cette interface TableModel . Vous 

pouvez construire un tableau à partir d'un tableau bidimensionnel d'objet et emballer automatiquement ce tableau dans un modèle par défaut (classe DefaultTableModel  ) en faisant juste appel au constructeur de JTable .

2. De même, il est possible de prévoir un modèle spécifique au nom donné par chacune des colonnes du tableau, qui se présente généralement sous forme grisé et 

en haut du tableau. Pour cela, vous devez implémenter l'interface  TableColumnModel . Toutefois, là aussi, il existe une classe  DefaultTableColumnModel  qui implémente cette interface et qui est directement intégré, sauf avis contraire, dans le composant  JTable .

3. Enfin, il existe un modèle prévu pous les sélections des éléments, issu de l'interface ListSelectionModel , dont la classe JTable possède également un modèle par 

défaut : DefaultListSelectionModel .

Phase de construction

Plusiseurs constructeurs sont aménagés pour résoudre les différentes possibilités de création de tableaux : 

1. JTable()  : Construit un tableau vierge avec le modèle de table par défaut, le modèle de colonne par défaut et le modèle de sélection par défaut.

2. JTable( int nombreLignes , int nombreColonnes  ) : Construit un tableau en spécifiant le nombre de lignes et de colonnes avec des cellules vierges avec les modèles 

par défaut.

3. JTable( Object[][] cellules , Object[]  nomColonnes  )  : Construit un tableau avec la valeur de chacune des cellules d'une part, et le nom donné à chacune des 

colonnes d'autre part, en prenant les modèles par défaut.

4. JTable( TableModel modèleTable  ) : Construit un tableau en proposant un nouveau modèle de table, mais en gardant le modèle de colonne par déafut et le modèle de 

sélection par défaut.

5. JTable( TableModel modèleTable , TableColumnModel  modèleColonne  ) : Construit un tableau en proposant un nouveau modèle de table et un nouveau modèle de 

colonne tout en gardant le modèle de sélection par défaut.

6. JTable( TableModel modèleTable , TableColumnModel modèleColonne , ListSelectionModel modèleSélection  ) : Construit un tableau en spécifiant les trois modèles.

7. JTable( Vector cellules , Vector  nomsColonne  )  : Construit un tableau à partir d'un verteur de vecteur pour le contenu des cellules et à partir d'un vecteur pour 

spécifier le nom des colonnes.

Mise en oeuvre d'un tableau simple

La figure ci-dessous montre un tableau typique, qui décrit les propriétés des planètes du système solaire. Une planète est considérée comme gazeuse si elle est composée principalement d'hydrogène et d'hélium.

codage correspondant 

package tables;

import  javax.swing.*;import  java.awt.*;

public class Tables extends JFrame {  private Object[][] cellules = {

{"Mercure", 2440.0, 0, false, Color.yellow},{"Vénus", 6052.0, 0, false, Color.yellow},{"Terre", 6378.0, 1, false, Color.blue},{"Mars", 3397.0, 2, false, Color.red},{"Jupiter", 71492.0, 16, true, Color.orange},{"Saturne", 60268.0, 18, true, Color.orange},

{"Uranus", 25559.0, 17, true, Color.blue},{"Neptune", 24766.0, 8, true, Color.blue},{"Pluton", 1137.0, 1, false, Color.black}

};  private String[] nomColonnes = {"Planète", "Rayon", "Satellites", "Gazeuse", "Couleur"};

  public Tables() {  super("Planètes");  add(new JScrollPane(new JTable(cellules, nomColonnes)));  pack();  setDefaultCloseOperation (EXIT_ON_CLOSE);  setVisible(true);

public static void main(String[] args) { new Tables(); }}

1. Comme vous pouvez le voir dans le code, les données de ce tableau sont enregistrées dans un tableau bidimensionnel de valeurs Object .

2. Nous profitons ici de l'autoboxing. Effectivement, les deuxièmes troisièmes et quatrièmes colonnes sont automatiquement converties en objet de type  Double ,Integer et Boolean .

3. Sinon, le tableau se contente d'invoquer la méthode toString() de chaque objet pour l'afficher. C'est ce qui se passe pour l'affichage de la classe  Color .

4. Le nom des colonnes sont fournies dans des chaînes séparées : String [] nomColonnes = { "Planète" , "Rayon" , "Satellites" , "Gazeuse" , "Couleur" }; 

Comportement minimal d'un tableau

Le tableau résultant possède déjà un comportement étonnament riche. Pour n'avoir saisie que très peu de code, nous obtenons énormément de fonctionnalités 

intéressantes prêtes à l'emploi.

Page 18: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 18/35

1. En-têtes des colonnes : Le composant JTable place automatiquement des en-têtes de colonnes sous une forme différente des cellules. Il apparaît clairement qu'ils 

ne font pas partie de la zone de données de la table.

2. Dépassement des cellules  : Lorsque la donnée d'une cellule est trop longue, elle est automatiquement tronquée et présentée an pointillé (...). C'est le cas des 

cellules de couleur de la colonne de droite : 

3. Le nom des colonnes toujours visible  : Recadrez le tableau verticalement jusqu'à ce que l'ascenceur vertical apparaisse et déplacez cet ascenceur. Les noms des 

colonnes restent constamment visibles.

4. Sélection de ligne  : Nous pouvons cliquer sur une cellule quelconque pour sélectionner la ligne entière. Cette fonction est contrôlable : nous pouvons choisir des 

cellules individuelles, des lignes ou des colonnes entières ou une combinaison des deux. Pour configurer la fonction sélection du composant  JTable , nous devons faire appel aux méthodes setCellSelectionEnabled() , setColumnSelectionAllowed() et setRowSelectionAllowed() .

5. Edition de cellule  : En double-cliquant sur une cellule, nous ouvrons une vue de modification ; elle propose alors un petit curseur de texte. Nous pouvons saisir ainsi directement dans la cellule le nouvelle valeur souhaitée.

6. Dimensionnement de colonne  : Lorsque nous déplaçons le curseur de la souris entre deux colonnes, nous obtenons un petit curseur en forme de flèches 

opposées. Cliquez et glissez pour modifier la largeur de la colonne. Suivant la configuration de  JTable , la largeur des autres colonnes est également succeptible de 

changer. La fonction de redimensionnement est contrôlée par la méthode  setAutoResizedMode() .

7. Réordonnancement des colonnes  : En cliquant et glissant sur un en-tête de colonne, nous pouvons déplacer la colonne entière à un autre endroit de la table.

Effectivement, cliquez sur l'un des noms de colonnes et déplacez-le à droite ou à gauche. La colonne entière se détache. Vous pouvez donc l'amener à un nouvel emplacement. Cela modifie uniquement l'affichage des colonnes. Le modèle des données n'est pas affecté.

Page 19: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 19/35

8. Imprimer un tableau : Depuis la version 5 de la JDK, vous envoyer tout le contenu d'un tableau directement à l'imprimante au moyen de la méthode  print()  de la classe JTable . Une boîte de dialogue d'impression s'affiche afin que vous choisissiez la bonne imprimante.

Méthodes intéressantes de la classe JTable 

void addColumn(TableColumn colonne)

void addColumnSelectionInterval(int de, int à)

void addRowSelectionInterval( int de, int à)

Ajout de lignes et de colonnes.

void clearSelection()

Désactive la sélection.

boolean editCellAt(int ligne, int colonne)

boolean editCellAt(int ligne, int colonne, EventObject événement)

Gestion de l'édition par programme.

int getColumnCount()

String getColumnName(int colonne)

boolean getColumnSelectionAllowed()

int getEditingColumn()

int getSelectedColumn()

int getSelectedColumnCount()

int[] getSelectedColumns()

void setColumnSelectionInterval(int de, int à)

void setEditingColumn(int colonne)

Gestion des colonnes.

int getRowCount()

int getRowHeight()

int getRowHeight(int ligne)

int getRowMargin()

boolean getRowSelectionAllowed()

int getEditingRow()

int getSelectedRow()

int getSelectedRowCount()

int[] getSelectedRows()

void setEditingRow(int ligne)

void setRowSelectionAllowed(boolean validation)

void setRowSelectionInterval(int de, int à)

Gestion des lignes.

TableModel getModel()

TableColumnModel getColumnModel()

ListSelectionModel getSelectionModel()

void setModel(TableModel modèle)

void setColumnModel(TableColumnModel modèle)

void setSelectionModel(ListSelectionModel modèle)Gestion des trois modèles par défaut de la table.

Object getValueAt(int ligne, int colonne)

void setValueAt(Object valeur, int ligne, int colonne)

Gestion des valeurs de cellules.

boolean isCellEditable(int ligne, int colonne)

boolean isCellSelected(int ligne, int colonne)

boolean isColumnSelected(int colonne)

boolean isEditing()

boolean isRowSelected(int ligne)

Quelques tests.

void moveColumn(int colonne, int colonneCible)

Déplace la colonne vers la nouvelle position.

boolean print()

boolean print(JTable.PrintMode mode)

boolean print(JTable.PrintMode mode, MessageFormat en-tête, MessageFormat pied)

boolean print(JTable.PrintMode mode, MessageFormat en-tête, MessageFormat pied, boolean boîteImpression, PrintRequestAttributeSet attr, boolean

interactive)

boolean print(JTable.PrintMode mode, MessageFormat en-tête, MessageFormat pied, boolean boîteImpression, PrintRequestAttributeSet attr, boolean

interactive, PrintService service)

Imprimer.

void removeColumn(TableColumn colonne)

void removeColumnSelectionInterval( int de, int à)

void removeEditor()

void removeRowSelectionInterval( int de, int à)

Suppressions.

void selectAll()

Sélectionner toutes les lignes.

void setAutoResizeMode(int mode)

Choisi le mode d'affichage automatique. Il existe 5 modes de réaffichage automatique :AUTO_RESIZE_OFF,AUTO_RESIZE_NEXT_COLUMN,AUTO_RESIZE_SUBSEQUENT_COLUMNS,AUTO_RESIZE_LAST_COLUMN,AUTO_RESIZE_ALL_COLUMNS.

Page 20: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 20/35

void setCellEditor(TableCellEditor éditeur)

void setDefaultEditor(Class<?> classColonne, TableCellEditor éditeur)

void setDefaultRenderer(Class<?> classColonne, TableCellRenderer rendu)

Proposer une gestion personnalisée de la table, respectivement l'éditeur et le rendu de la cellule.

void setCellSelectionEnabled(boolean validation)

void setColumnSelectionAllowed(boolean validation)

void setColumnSelectionAllowed(boolean validation)

void setShowGrid(boolean grille)

void setShowHorizontalLines(boolean horizontal)

void setShowVerticalLines(boolean vertical)

Validations.

void setGridColor(Color couleur)

void setSelectionBackground(Color fond)

void setSelectionForeground(Color texte)

Gestion des couleurs.

void setIntercellSpacing(Dimension intervalle)

void setPreferredScrollableViewportSize(Dimension dimension)

void setRowHeight(int hauteur)

void setRowHeight(int ligne, int hauteur)

void setRowMargin(int marge)

Réglagesdes différentes dimensions.

void sorterChanged(RowSorterEvent événement)

void tableChanged(TableModelEvent événement)

void valueChanged(ListSelectionEvent événement)

void editingCanceled( ChangeEvent événement)

void editingStopped(ChangeEvent événement)

Notifications.

Utiliser le modèle de table par défaut

Il est possible de travailler avec le modèle par défaut du tableau ( DefaultTableModel  ) afin d'introduire le contenu de chacune des cellules ultérieurement (ainsi que le nom des colonnes), c'est-à-dire après la création de la table. Pour utiliser toutes les compétences de ce modèle, faites appel à la méthode  getModel() du composant JTable .

Méthodes spécifiques à la classe DefaultTableModel 

DefaultTableModel()

DefaultTableModel( int nombreLignes, int nombreColonnes).

DefaultTableModel( Object[][] données, Object[] nomColonnes)

DefaultTableModel( Object[] nomColonnes, int nombreLignes)

DefaultTableModel( Vector nomColonnes, int nombreLignes)

DefaultTableModel( Vector données, Vector nomColonnes)

Ensemble de constructeurs relativement similaires à la classe JTable.

void addColumn(Object nomColonnes)

void addColumn(Object nomColonnes, Object[] valeursDeLaColonne)

void addColumn(Object nomColonnes, Vector valeursDeLaColonne)

Ajoute une nouvelle colonne au modèle avec éventuellement des valeurs.

void addRow(Object[] valeursDeLaNouvelleLigne )

void addRow(Vector valeursDeLaNouvelleLigne )

Ajoute une nouvelle ligne avec l'ensemble des valeurs proposées en argument.

int getRowCount()

int getColumnCount()

Retourne le nombre de lignes et de colonnes.

String getColumnName(int numéroColonne)

Retourne le nom de la colonne suivant la position spécifiée en argument.

Vector getDataVector()

Retourne le vecteur de vecteur de l'ensemble des valeurs pour chacune des cellules.

Object getValueAt(int ligne, int colonne) .

Retourne la valeur de la cellule se situant aux coordonnées du tableau spécifiées en argument de la méthode.

void insertRow(int ligne, Object[] donnéesDeLaLigne )

void insertRow(int ligne, Vector donnéesDeLaLigne )

Insère une nouvelle ligne dans le modèle avec l'ensemble des valeurs spécifiées.

boolean isCellEditable(int ligne, int colonne)

Contrôle si la cellule spécifiée est éditable ou pas.

void moveRow(int début, int fin, int nouvellePosition)

Un ensemble de lignes peuvent être déplacées dans le modèle.

void removeRow(int ligne)

Supprime la ligne du modèle

void setColumnCount(int nombreColonnes)

Prévoit un certaine nombre de colonnes au modèle.

void setColumnIdentifiers( Object[] nouveauxNomsDeColonne )

void setColumnIdentifiers( Vector nouveauxNomsDeColonne )

Spécifie le nom de chaque colonne.

Page 21: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 21/35

void setDataVector(Object[][] données, Object[] nomColonnes)

void setDataVector(Vector données, Vector nomColonnes)

Spécifie la valeur de chacune des cellules ainsi que le nom de chaque colonne.

void setRowCount(int nombreLignes)

Prévoit un certain nombre de lignes.

void setValueAt(Object uneValeur, int ligne, int colonne)

Propose une nouvelle valeur à une cellule.

A titre d'exemple, je vous propose de réaliser un tableau dynamique qui recense l'ensemble des fichiers stockés dans le répertoire  "C:\Photos\" . Dans ce tableau apparaît repectivement, le nom du fichier, son extension, son nombre d'octets, savoir s'il s'agit d'une image avec les dimensions de cette image.

codage correspondant 

package tables;

import  java.awt.Image;import  java.io.File;

import  javax.swing.*;import  javax.swing.table.*;

public class Tables extends JFrame {  private String[] colonnes = {"Nom fichier", "Extensions", "Octets", "Image ?", "Largeur", "Hauteur"};  private JTable table = new JTable();

  public Tables() {  super("Liste des fichiers");  construireTableau ();  add(new JScrollPane(table));  pack();  setDefaultCloseOperation (EXIT_ON_CLOSE);  setVisible(true);

private void construireTableau () {  File[] fichiers = new File("C:/Photos/").listFiles();  DefaultTableModel  modèle = (DefaultTableModel) table.getModel();  modèle.setColumnIdentifiers(colonnes);

  for (File fichier : fichiers) {  String[] découpage = fichier.getName().split("\\.");  String libellé = découpage[0];  String extension = découpage[1].toUpperCase();  long taille = fichier.length();  Image image = new ImageIcon(fichier.getPath()).getImage();

boolean isImage = image.getWidth(null) != -1;  modèle.addRow(new Object[]{libellé, extension, taille, isImage, image.getWidth(null), image.getHeight(null)});

}}

 public static void main(String[] args) { new Tables(); }

}

 

Modèles de tableaux 

JTable est un composant très puissant. Il propose gracieusement de nombreuses fonctions. Cependant, la configuration par défaut n'est généralement pas conseillé.

La stratégie suivie ne correspond pas vraiment à ce que nous désirons faire dans ces exemples. En particulier, nous souhaitons des entrées en lecture seule : elle ne doivent pas être modifiables. De même, nous aimerions que les entrées de la colonne "Image ?" soient des cases à cocher et non des termes anglais. Enfin, l'idéal serait de pouvoir visualiser les fichiers qui sont des images.

La classe abstraite AbstractTableModel

Pour obtenir une plus grande souplesse de  JTable , il vaut mieux implémenter votre propre modèle de tableau au lieu de placer toutes vos données dans un tableau bidimensionnel pour les afficher sous forme de tableau. Il suffit donc de personnaliser vos données en écrivant votre modèle de table en implémentant l'interface 

TableModel . Par chance, Swing nous facilite la tâche en proposant la classe abstraite  AbstractTableModel faisant le gros du travail. Dans notre modèle personnalisé, il suffit 

ainsi de créer une classe qui hérite de cette classe AbstractTableModel et de redéfinir les trois méthodes abstraites suivantes : 

1. int getRowCount()  : cette méthode renvoie le nombre de ligne de la table.

2. int getColumnCount() : cette méthode renvoie le nombre de colonne de la table.

3. Object getValueAt( int ligne , int colonne  ) : cette méthode renvoie la valeur de la cellule désignée.

Il existe plusieurs manières d'implémenter la méthode  getValueAt() . Vous pouvez simplement calculer la réponse, ou chercher la valeur dans une base de données,

ou encore dans une autre source de données.

Lorsque JTable  a besoin de valeurs de données, il appelle la méthode getValueAt() du modèle de la table. Pour connaître la taille globale de la table, il appelle les 

méthodes getRowCount() et getColumnCount() de ce modèle de table.

Nom des colonnes

Si vous ne définissez pas de noms de colonnes, la méthode getColumnName()  du modèle AbstractTableModel choisi comme nom  A, B, C, etc . Pour modifier le nom des colonnes, il suffit de surcharger la méthode getColumnName() .

Gestion de l'affichage suivant le type de colonne

Page 22: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 22/35

Par défaut, AbstractTableModel rend toutes les cellules non modifiables, ce qui correspond à ce que nous voulions. Aucune modification n'est nécessaire à ce sujet. Pour proposer une visualisation personnalisée suivant le type à représenter, le tableau doit posséder plus d'informations sur les types des colonnes. Pour cela, vous devez nous 

redéfinir la méthode getColumnClass() de votre modèle de tableau personnalisé (issu de la classe AbstractTableModel  ), qui renvoie la classe qui décrit le type de la colonne.

La classe JTable choisira alors un afficheur approprié pour cette classe : 

1. Icon : génère automatiquement une image.

2. Boolean : génère automatiquement des cellules de type case à cocher.

3. Numériques : génère automatiquement une valeur numérique de type entier, de type réel, etc.

4. Object : génère une chaîne de caractères en faisant appel à la méthode  toString() .

Pour les autres types, vous pouvez fournir vos propres afficheurs de cellule. Les afficheurs de cellules de tableau sont comparables aux afficheurs de cellules d'arbre. Ce sujet sera traité dans le chapitre suivant.

Exemple d'application de modèle personnalisé

A titre d'exemple, je vous propose de reprendre l'application précédente et de créer un nouveau modèle personnalisé qui permettra en autre de prévoir des cases à cocher 

et d'afficher des vignettes pour les fichiers image.

codage correspondant 

package tables;

import  java.awt.Image;import  java.io.File;import  java.text.DecimalFormat;import  javax.swing.*;import  javax.swing.table.*;

public class Tables extends JFrame {  private ModèleTableau modèle = new ModèleTableau();  private JTable tableau = new JTable(modèle);

  public Tables() {  super("Liste des fichiers");  tableau.setRowHeight(70);  add(new JScrollPane(tableau));  pack();  setDefaultCloseOperation (EXIT_ON_CLOSE);  setVisible(true);

public static void main(String[] args) { new Tables(); } 

private class ModèleTableau extends AbstractTableModel {  private String[] colonnes = {"Nom fichier", "Extension", "Poids", "Image ?", "Dimension", "Vue"};  private File[] fichiers = new File("C:/Photos/").listFiles();  private Object[] lignes = new Object[fichiers.length];

  public ModèleTableau () {for (int i=0; i<fichiers.length; i++) {

  File fichier = fichiers[i];  String[] découpage = fichier.getName().split("\\.");  String libellé = découpage[0];  String extension = découpage[1].toUpperCase();  long taille = fichier.length();  Image image = new ImageIcon(fichier.getPath()).getImage();

Icon icône = new ImageIcon(image.getScaledInstance(-1, 70, Image.SCALE_DEFAULT));  boolean isImage = image.getWidth(null) != -1;  String dimension = isImage ? image.getWidth(null)+" x "+image.getHeight(null) : "Aucune";

lignes[i] = new Object[]{libellé, extension, taille, isImage, dimension, icône};}

}

  public int getRowCount() {  return fichiers.length;

}

  public int getColumnCount() {  return colonnes.length;

}

  public Object getValueAt(int ligne, int colonne) {  Object[] fichier = (Object[]) lignes[ligne];  return fichier[colonne];

Page 23: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 23/35

}

@Override  public Class<?> getColumnClass(int colonne) {  Object[] première = (Object[]) lignes[0];  return première[colonne].getClass();

}

@Override  public String getColumnName(int colonne) {  return colonnes[colonne];

}}

}

 

Rendu de cellule personnalisé 

La classe JTable est déjà capable de maîtriser un certain nombre d'affichages prédéfinis, comme les cases à cocher et les icônes. Toutefois, il peut être intéressant d'aller plus loin dans cette démarche et de prévoir, par exemple, un formatage particulier pour les valeurs numériques.

Pour les types autre que les classes  Icon et Boolean , vous devez fournir votre propre afficheur de cellules. Les afficheurs de cellules de tableau sont comparables aux afficheurs de cellules d'arbre, que nous avons déjà abordés. Ils doivent implémenter l'interface  TableCellRenderer , qui comprend une seule méthode 

getTableCellRendererComponent() que vous devez donc redéfinir pour adapter l'affichage à votre convenance.

Interface TableCellRenderer 

Component getTableCellRendererComponent( JTable table , Object valeur , boolean sélectionné , boolean focus , int ligne , int colonne  ) 

Méthode à redéfinir lorsque nous souhaitons proposer un rendu personnalisé pour chaque noeud.

Cette méthode est appelée lorsque la table doit afficher une cellule. Vous devez renvoyer un composant dont la méthode  paint() est invoqué pour dessiner 

la cellule.

Une fois que vous avez créer notre afficheur de cellule en créant une classe qui implémente l'interface TableCellRenderer, vous devrez demander au tableau d'utiliser cet afficheur pour tous les objets qui correspond à la classe que vous souhaitez prendre en compte. La méthode setDefaultRenderer() de 

la classe JTable permet d'effectuer cette association. Il suffit de fournir un objet Class et l'afficheur : 

table. setDefaultRenderer(  Integer.class , afficheur  );

Cet afficheur est maitnenant utilisé pour tous les objets du type spécifié.

§ 

Mise en oeuvre

Je vous propose de réorganiser l'application précédente afin que le poids de chaque fichier soit exprimé en octets avec la prise en compte de la séparation des milliers afin 

que l'affichage soit plus agréable. Par ailleurs, les fichiers qui ne sont pas desimages sont pas très visibles dans le tableau, je propose donc de renforcer la case à cocher 

afin d'établir un code de couleur suivant la qualité du fichier.

codage correspondant 

package tables;

import  java.awt.*;import  java.io.File;

import  java.text.DecimalFormat;import  javax.swing.*;import  javax.swing.table.*;

public class Tables extends JFrame {  private ModèleTableau modèle = new ModèleTableau();  private JTable tableau = new JTable(modèle);

  public Tables() {  super("Liste des fichiers");  tableau.setRowHeight(70);  tableau.setDefaultRenderer (Long.class, new RenduEntier());  tableau.setDefaultRenderer (Boolean.class, new RenduCaseACocher());

Page 24: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 24/35

  add(new JScrollPane(tableau));  pack();  setDefaultCloseOperation (EXIT_ON_CLOSE);  setVisible(true);

public static void main(String[] args) { new Tables(); } 

private class ModèleTableau extends AbstractTableModel {  private String[] colonnes = {"Nom fichier", "Extension", "Poids", "Image ?", "Dimension", "Vue"};  private File[] fichiers = new File("C:/Photos/").listFiles();  private Object[] lignes = new Object[fichiers.length];

  public ModèleTableau () {for (int i=0; i<fichiers.length; i++) {

  File fichier = fichiers[i];  String[] découpage = fichier.getName().split("\\.");  String libellé = découpage[0];  String extension = découpage[1].toUpperCase();

  long taille = fichier.length();  Image image = new ImageIcon(fichier.getPath()).getImage();Icon icône = new ImageIcon(image.getScaledInstance(-1, 70, Image.SCALE_DEFAULT));

  boolean isImage = image.getWidth(null) != -1;  String dimension = isImage ? image.getWidth(null)+" x "+image.getHeight(null) : "Aucune";  lignes[i] = new Object[]{libellé, extension, taille, isImage, dimension, icône};

}}

  public int getRowCount() {  return fichiers.length;

}

  public int getColumnCount() {  return colonnes.length;

}

  public Object getValueAt(int ligne, int colonne) {  Object[] fichier = (Object[]) lignes[ligne];  return fichier[colonne];

}

@Override  public Class<?> getColumnClass(int colonne) {  Object[] première = (Object[]) lignes[0];  return première[colonne].getClass();

}

@Override  public String getColumnName(int colonne) {  return colonnes[colonne];

}}

 private class RenduEntier  extends JFormattedTextField  implements TableCellRenderer {

  public RenduEntier() {  super(new DecimalFormat("#,##0 octets"));  setHorizontalAlignment (RIGHT);

public Component getTableCellRendererComponent (JTable table, Object valeur, boolean sélectionné, boolean focus, int ligne, int colonne) {  setValue(valeur);  return this;

}}

 private class RenduCaseACocher  extends JCheckBox implements TableCellRenderer {

  public Component getTableCellRendererComponent (JTable table, Object valeur, boolean sélectionné, boolean focus, int ligne, int colonne) {  setSelected((Boolean)valeur);  setBackground(isSelected() ? Color.GREEN : Color.RED);  return this;

}}

}

 

Modifier une cellule de tableau 

Pour qu'une cellule soit modifiable, le modèle de tableau doit indiquer quelles cellules sont modifiables en définissant la méthode  isCellEditable() . La plupart du temps,vous choisirez de rendre certaines colonnes modifiables.

L' AbstractTableModel définit la méthode  isCellEditable()  de sorte qu'elle renvoie toujours false . Le DefaultTableModel  surchage cette méthode pour qu'elle renvoie toujours true .

A titre d'exemple, je reprends l'application précédente dans laquelle je supprime la visualisation des fichiers image et où je rajoute l'édition possible du nom defichier.

Page 25: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 25/35

codage correspondant 

package tables;

import  java.awt.*;import  java.io.File;import  java.text.DecimalFormat;import  javax.swing.*;import  javax.swing.table.*;

public class Tables extends JFrame {  private ModèleTableau modèle = new ModèleTableau();  private JTable tableau = new JTable(modèle);

  public Tables() {  super("Liste des fichiers");  tableau.setDefaultRenderer (Long.class, new RenduEntier());  tableau.setDefaultRenderer (Boolean.class, new RenduCaseACocher());  add(new JScrollPane(tableau));

  pack();  setDefaultCloseOperation (EXIT_ON_CLOSE);  setVisible(true);

public static void main(String[] args) { new Tables(); } 

private class ModèleTableau extends AbstractTableModel {  private String[] colonnes = {"Nom fichier", "Extension", "Poids", "Image ?", "Dimension", "Vue"};  private File[] fichiers = new File("C:/Photos/").listFiles();  private Object[] lignes = new Object[fichiers.length];

  public ModèleTableau () {for (int i=0; i<fichiers.length; i++) {

  File fichier = fichiers[i];  String[] découpage = fichier.getName().split("\\.");  String libellé = découpage[0];  String extension = découpage[1].toUpperCase();  long taille = fichier.length();  Image image = new ImageIcon(fichier.getPath()).getImage();

boolean isImage = image.getWidth(null) != -1;  String dimension = isImage ? image.getWidth(null)+" x "+image.getHeight(null) : "Aucune";  lignes[i] = new Object[]{libellé, extension, taille, isImage, dimension};

}}

  public int getRowCount() {  return fichiers.length;

}

  public int getColumnCount() {  return colonnes.length;

}

  public Object getValueAt(int ligne, int colonne) {  Object[] fichier = (Object[]) lignes[ligne];  return fichier[colonne];

}

  public void setValueAt(Object valeur, int ligne, int colonne) {  Object[] fichier(Object[]) lignes[ligne];  fichier[colonne] = valeur;

}

@Override  public Class<?> getColumnClass(int colonne) {  Object[] première = (Object[]) lignes[0];  return première[colonne].getClass();

}

@Override  public String getColumnName(int colonne) {  return colonnes[colonne];

}

@Override  public boolean isCellEditable(int ligne, int colonne) {  return colonne == 0;

}}

 

private class RenduEntier  extends JLabel implements TableCellRenderer {  public RenduEntier() {  setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));  setHorizontalAlignment (RIGHT);

public Component getTableCellRendererComponent (JTable table, Object valeur, boolean sélectionné, boolean focus, int ligne, int colonne) {  DecimalFormat décimal = new DecimalFormat("#,##0 octets");  setText(décimal.format(valeur));  return this;

}}

 private class RenduCaseACocher  extends JCheckBox implements TableCellRenderer {

  public Component getTableCellRendererComponent (JTable table, Object valeur, boolean sélectionné, boolean focus, int ligne, int colonne) {  setSelected((Boolean)valeur);  setBackground(isSelected() ? Color.GREEN : Color.RED);  return this;

}}

}

Attention , si vous désirez que votre saisie soit prise en compte, vous devez impérativement redéfinir la méthode  setValueAt()  de votre modèle de tableau afin que la nouvelle valeur s'intègre au bon endroit dans votre enregistrement bidirectionnel, ici l'objet lignes qui est un tableau de tableau d' Object .

Editeur de cellule par défaut

Le composant JTable  possède un éditeur de cellule par défaut issu de la classe DefaultCellEditor  qui implémente l'interface  TableCellEditor . Un  DefaultCellEditor  peut être construit avec un JTextField , un JCheckBox ou un JComboBox (ou leurs fils). JTable  installe automatiquement un éditeur de cases à cocher pour les cellules booléennes et 

un éditeur de champ de texte pour les cellules qui ne fournissent pas leur propre afficheur. Les champs de texte permettent à l'utilisateur de modifier les chaînes de 

caractères qui ont été créées en appliquant la méthode  toString() à la valeur de retour de la méthode getValueAt() du modèle de tableau.

Page 26: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 26/35

Lorsque la modification est terminée, la valeur modifiée est récupérée par un appel à la méthode getCellEditorValue()  de la classe DefaultCellEditor . Cette méthode 

doit renvoyer une valeur de type correct (à savoir le type renvoyé par la méthode  getColumnType() du modèle).

Pour obtenir un éditeur de menu déroulant ( JComboBox  ), vous devez définir manuellement l'éditeur de la cellule. En effet, le composant  JTable n'a aucune idée des valeurs appropriées pour un type particulier.

J'aimerais par exemple changer l'extension d'un fichier image afin de permettre ainsi la conversion vers un autre type de compression.

Voici le code d'initialisation de ce menu déroulant : 

 JComboBox extensions = new   JComboBox ( new  String[]{ "PNG"  , "GIF"  , "JPG" });

Pour créer ensuite un DefaultCellEditor , il suffit de fournir le menu déroulant au construteur : 

TableCellEditor  éditeur = new  DefaultCellEditor (extensions);

Nous devons installer ensuite l'éditeur.

Contrairement aux afficheurs de cellules du poids du fichier, cet éditeur ne dépend pas du type de l'objet : nous ne voulons pas forcément l'utiliser pour 

tous les objets de type String . Au contraire, nous devrons l'installer dans une colonne particulière.

La classe JTable  enregistre des informations sur les colonnes du tableau dans des objets de type TableColumn . Un objet TableColumnModel  gère les colonnes. Si vous ne voulez pas insérer ou supprimer de colonnes dynamiquement, vous ne vous servirez pas beaucoup du modèle de colonnes du tableau. Cependant, pour 

obtenir un objet TableColumn particulier, vous devez passer par le modèle de colonnes et lui demander l'objet de colonne correspondant : 

TableColumnModel  modèleColonne = tableau. getColumnModel() ;TableColumn colonne = modèleColonne. getColumn( 4 );

Pour terminer, vous pouvez installer l'éditeur de cellules : 

tableau. setCellEditor( éditeur  ) ;

Si vos cellules sont plus hautes que les cellules par défaut, vous devrez également définir la hauteur de la ligne : 

tableau. setRowHeight( hauteur  ) ;

Par défaut, toutes les lignes d'un tableau ont la même hauteur. Vous pouvez toutefois définir les hauteurs de lignes individuelles en appelant : 

tableau. setRowHeight( ligne , hauteur  ) ;

La hauteur de ligne réelle est égale à la hauteur de ligne qui a été définie par ces méthodes, réduite de la marge de la ligne. La marge de ligne par défaut est 

de 1, mais vous pouvez également la modifier par l'appel : 

tableau. setRowMargin( marge ) ;

Pour afficher une icône dans l'en-tête, définissez la valeur de l'en-tête : 

colonne. setHeaderValue( new ImageIcon( "extension.gif"  )) ;

L'en-tête de tableau n'est toutefois pas suffisant pour choisir un afficheur approprié pour la valeur de l'en-tête. Vous devez installer l'afficheur 

manuellement. Par exemple, pour afficher une icône d'image dans un en-tête de colonne, appelez : 

colonne. setHeaderRenderer( tableau. getDefaultRenderer(  ImageIcon.class )) 

Mise en oeuvre de modification de contenu de cellule

Nous allons prendre encore une fois l'application précédente en proposant toutefois pas mal de modifications. Cette fois-ci, seuls les fichiers images seront visibles dans le 

tableau, et il sera surtout possible de retoucher les images déjà stockées sur le disque dur en proposant de changer le nom du fichier, le format de l'image (type de compression) ainsi que les nouvelles dimensions en respectant le ratio de l'image, c'est-à-dire le rapport entre la largeur et la hauteur.

Page 27: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 27/35

codage correspondant 

package tables;

import  java.awt.*;import  java.awt.event.ActionEvent;import  java.awt.geom.*;import  java.awt.image.*;import  java.io.*;import  java.text.DecimalFormat;import  javax.imageio.ImageIO;import  javax.swing.*;import  javax.swing.table.*;

public class Tables extends JFrame {  private ModèleTableau modèle = new ModèleTableau();  private JTable tableau = new JTable(modèle);  private JComboBox extensions = new JComboBox(new String[]{"PNG", "GIF", "JPG"});  private JToolBar valider = new JToolBar();

  public Tables() {  super("Liste des fichiers");

  TableColumnModel modèleColonne = tableau.getColumnModel();modèleColonne.getColumn(1).setCellEditor(new DefaultCellEditor (extensions));  tableau.setDefaultRenderer (Long.class, new RenduEntier());  tableau.setRowHeight(52);  add(new JScrollPane(tableau));

valider.add(new AbstractAction("Valider changement de la ligne sélectionnée") {  public void actionPerformed(ActionEvent e) {

modèle.changerFichier(tableau.getSelectedRow());}

});  add(valider, BorderLayout.NORTH);  pack();  setDefaultCloseOperation (EXIT_ON_CLOSE);  setVisible(true);

public static void main(String[] args) { new Tables(); } 

private class ModèleTableau extends AbstractTableModel {  private String[] colonnes = {"Nom fichier", "Extension", "Poids", "Largeur", "Hauteur", "Image"};  private File[] fichiers;  private Object[] lignes; 

public ModèleTableau () {constituer();

private void constituer() {  fichiers = new File("C:/Photos/").listFiles(new FileFilter() {  public boolean accept(File fichier) {  String nom = fichier.getName();  String[] découpage = fichier.getName().split("\\.");  String extension = découpage[1].toUpperCase();

return extension.equals("GIF") || extension.equals("PNG") || extension.equals("JPG");}

});  lignes = new Object[fichiers.length];  for (int i=0; i<fichiers.length; i++) {  File fichier = fichiers[i];  String[] découpage = fichier.getName().split("\\.");  String extension = découpage[1].toUpperCase();

long taille = fichier.length();  BufferedImage image = null;

Icon icône = null;  try {

image = ImageIO.read(fichier);icône = new ImageIcon(image.getScaledInstance(-1, 50, Image.SCALE_DEFAULT));

}catch (IOException ex) { }

  lignes[i] = new Object[]{découpage[0], extension, taille, image.getWidth(), image.getHeight(), icône, image};}

}

@Override

Page 28: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 28/35

  public int getRowCount() {  return fichiers.length;

@Override  public int getColumnCount() {  return colonnes.length;

}

@Override  public Object getValueAt(int ligne, int colonne) {  Object[] image = (Object[]) lignes[ligne];  return image[colonne];

}

@Override  public void setValueAt(Object valeur, int ligne, int colonne) {  Object[] image = (Object[]) lignes[ligne];  if (colonne==3) {

  double largeur = (Integer) image[3];  double hauteur = (Integer) image[4];  double ratio = (double)largeur / hauteur;  double récupération = (Integer) valeur;  image[4] = (int)(récupération / ratio);

tableau.repaint();}image[colonne] = valeur;

}

@Override  public Class<?> getColumnClass(int colonne) {  Object[] première = (Object[]) lignes[0];  return première[colonne].getClass();

}

@Override  public String getColumnName(int colonne) {  return colonnes[colonne];

}

@Override  public boolean isCellEditable(int ligne, int colonne) {  return colonne==0 || colonne==1 || colonne==3 ;

public void changerFichier(int ligne) {  Object[] infos = (Object[]) lignes[ligne];

BufferedImage image = (BufferedImage) infos[6];  String nom = (String) infos[0];  String extension = (String) infos[1];  int largeur = (Integer) infos[3];  int hauteur = (Integer) infos[4];  double ratio = (double)largeur / image.getWidth();

BufferedImage traitement = new BufferedImage(largeur, hauteur, image.getType());  AffineTransform retailler = AffineTransform.getScaleInstance(ratio, ratio);  int interpolation = AffineTransformOp.TYPE_BICUBIC ;  AffineTransformOp retaillerImage = new AffineTransformOp(retailler, interpolation);

retaillerImage.filter(image, traitement);  try {  ImageIO.write(traitement, extension, new File("C:/Photos/" + nom + '.' + extension));  constituer();  tableau.revalidate();

}catch (IOException ex) {}

}}

 private class RenduEntier  extends DefaultTableCellRenderer {

  public RenduEntier() {  setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));  setHorizontalAlignment (RIGHT);

@Override  public Component getTableCellRendererComponent (JTable table, Object valeur, boolean sélectionné, boolean focus, int ligne, int colonne) {  DecimalFormat décimal = new DecimalFormat("#,##0 octets");  setText(décimal.format(valeur));  if (sélectionné) setBackground(tableau.getSelectionBackground ());  else setBackground(Color.WHITE);  return this;

}}

Editeur de cellules de tableau personnalisé

Nous pouvons aller encore plus loin dans cette démarche. Nous pouvons, par exemple, faire en sorte que notre nom de fichier image soit éditer automatiquement au 

travers d'un sélecteur de fichier.

Page 29: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 29/35

L'éditeur de nom de fichier ne doit pas être un éditeur de cellule standard, mais une implémentation personnalisée. Pour créer un éditeur de cellules personnalisé,vous devez implémenter l 'interface TableCellEditor . Cette interface est assez pénible à utiliser. Heureusement, une classe AbstractCellEditor  est fournie pour gérer 

les détails de l'événement.

Interface TableCellEditor 

Pour maîtriser la gestion de votre éditeur personnalisé, vous devez redéfinir un certain nombre de méthodes issues de cette interface  TableCellEditor : 

1. getTableCellEditorComponent() : La méthode getTableCellEditorComponent() de l'interface TableCellEditor a besoin d'un composant pour afficher une cellule. C'est 

exactement comme pour la méthode getTableCellRendererComponent()  de l'interface TableCellRenderer , sauf qu'il n'y a cette fois aucun paramètre focus . Comme 

la cellule doit être modifiée, elle est censée posséder le focus.

Dans le cas d'un menu déroulant, le composant de l'éditeur remplace temporairement l'afficheur.§ 

2. isCellEditable()  : La classe  JTable  appelle votre éditeur avec un événement (comme un clic de souris) pour déterminer si cet événement est acceptable pour initialiser le processus de modification. Vous pouvez vous occuper de la gestion événementielle au travers de la méthode  isCellEditable()  de l'interface 

TableCellEditor . Nous pouvons par exemple accepter tous les événements.

@Override  public  boolean isCellEditable( EventObject  événement  ) {   return true ;}

Cependant, si cette méthode renvoie false , le tableau n'insérera pas le composant de l'éditeur.

§ 

3. shouldSelectCell()  : Une fois que le composant de l'éditeur est installé, la méthode  shouldSelectCell()  est appelée, avec le même événement. Le processus de 

modification doit commencer dans cette méthode, par exemple en affichant la fenêtre de sélection de fichier.

@Override  public  boolean  shouldSelectCell ( EventObject  événement  ) { 

sélecteur. showDialog(  parent  , libelléBouton )return true ;

}

4. cancelCellEditing() et stopCellEditing()  : Si l'utilisateur doit annuler la modification en cours, le tableau appelle la méthode  cancelCellEditing() . Si l'utilisateur a cliqué sur une autre cellule du tableau, il appelle la méthode  stopCellEditing() . Lorsque votre méthode  stopCellEditing()  est appelée, le tableau peut essayer 

d'utiliser la valeur en cours de modification. C'est pourquoi, il ne faut renvoyer  true que lorsque la valeur courante est valide. Pour notre sélecteur de fichier, il peut 

être judicieux de vérifier que seules les valeurs correctes soient renvoyées à l'éditeur.

Vous devrez aussi appeler les méthodes de superclasse pour déclencher des événements, faute de quoi la modification ne fonctionnera pas 

correctement.

@Override  public  void   stopCellEditing () { 

...super. stopCellEditing();

}

5. getCellEditorValue() : Enfin, vous devez dedéfinir la méthode getCellEditorValue() afin qu'elle délivre la valeur fournie par l'utilisateur au cours de la procédure de modification : 

@Override  public  Object   getCellEditorValue() { 

return sélecteur. getSelectedFile(). getName() ;}

Pour résumer 

Pour résumer, votre éditeur personnalisé doit : 

1. Etendre la classe AbstractCellEditor et implémenter l'interface TableCellEditor .

Page 30: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 30/35

2. Définir la méthode getTableCellEditorComponent() pour fournir un composant qui va représenté votre valeur. Il peut s'agir d'un composant  dummy (si vous afficher une boîte de dialogue) ou un composant pour la modification sur place, comme un menu déroulant ou un champ de texte.

3. Définir les méthodes shouldSelectCell() , stopCellEditing()  et  cancelCellEditing() pour gérer le début, la réalisation et l'annulation de la procédure de modification.

stopCellEditing() et cancelCellEditing() doivent appeler les méthodes de la superclasse pour s'assurer que les écouteurs sont avertis 

4. Définir la méthode getCellEditorValue() pour renvoyer la valeur qui résulte de la procédure d'édition.

5. Enfin, vous devez indiquer le moment où l'utilisateur a terminé la modification en appelant explicitement les méthodes stopCellEditing() et/ou cancelCellEditing() .

Prise en compte de la modification 

Lorsque la modification est terminée, la classe JTable  appelle la méthode  setValueAt()  du modèle de tableau. Comme nous l'avons déjà évoqué au début de ce chapitre,

vous devez impérativement surcharger cette méthode pour enregistrer la nouvelle valeur.

void setValue(Object valeur, int ligne, int colonne);

Le paramètre valeur correspond à l'objet renvoyé par l'éditeur de cellules.

1. Si vous avez implémenté l'éditeur de cellule, vous connaissez le type d'objet renvoyé par la méthode getCellEditorValue() .

2. Dans le cas de DefaultCellEditor , il existe trois possibilités pour cette valeur. Il s'agit d'un Boolean  si la cellule concernée est une case à cocher et d'une chaîne 

pour un champ de texte. Si la valeur provient d'un menu déroulant, il s'agit alors de l'objet sélectionné par l'uitlisateur.

Si l'objet valeur  n'est pas du type approprié, vous devrez le convertir. Cela se produit le plus souvent lorsqu'un nombre est modifié dans un champ de texte. Dans 

notre exemple, nous plaçons des chaînes de caractères formatées suivant le motif proposé par la classe DecimalFormat pour la colonne Poids .

codage correspondant 

package tables;

import  java.awt.*;import  java.awt.event.ActionEvent;import  java.awt.geom.*;import  java.awt.image.*;import  java.io.*;import  java.text.DecimalFormat;

import  java.util.EventObject;import  javax.imageio.ImageIO;import  javax.swing.*;import  javax.swing.filechooser.FileNameExtensionFilter ;import  javax.swing.table.*;

public class Tables extends JFrame {  private ModèleTableau modèle = new ModèleTableau();  private JTable tableau = new JTable(modèle);  private JComboBox extensions = new JComboBox(new String[]{"PNG", "GIF", "JPG"});  private JToolBar valider = new JToolBar();

  public Tables() {  super("Liste des fichiers");  TableColumnModel modèleColonne = tableau.getColumnModel();

modèleColonne.getColumn(1).setCellEditor(new DefaultCellEditor (extensions));modèleColonne.getColumn(0).setCellEditor(new EditeurCellule());

  tableau.setRowHeight(52);  add(new JScrollPane(tableau));

valider.add(new AbstractAction("Valider changement de la ligne sélectionnée") {

  public void actionPerformed(ActionEvent e) {modèle.changerFichier(tableau.getSelectedRow());}

});  add(valider, BorderLayout.NORTH);  pack();  setDefaultCloseOperation (EXIT_ON_CLOSE);  setVisible(true);

public static void main(String[] args) { new Tables(); } 

private class ModèleTableau extends AbstractTableModel {  private String[] colonnes = {"Nom fichier", "Extension", "Poids", "Largeur", "Hauteur", "Image"};  private File[] fichiers;  private Object[] lignes; 

public ModèleTableau () {constituer();

private void constituer() {  fichiers = new File("C:/Photos/").listFiles(new FileFilter() {  public boolean accept(File fichier) {  String nom = fichier.getName();  String[] découpage = fichier.getName().split("\\.");  String extension = découpage[1].toUpperCase();

return extension.equals("GIF") || extension.equals("PNG") || extension.equals("JPG");}

});  lignes = new Object[fichiers.length];  for (int i=0; i<fichiers.length; i++) {  File fichier = fichiers[i];  String[] découpage = fichier.getName().split("\\.");  String extension = découpage[1].toUpperCase();

long taille = fichier.length();  DecimalFormat décimal = new DecimalFormat("#,##0 octets");  BufferedImage image = null;

Icon icône = null;  try {

image = ImageIO.read(fichier);icône = new ImageIcon(image.getScaledInstance(-1, 50, Image.SCALE_DEFAULT));

}catch (IOException ex) { }

  lignes[i] = new Object[]{découpage[0], extension, décimal.format(taille), image.getWidth(), image.getHeight(), icône, image};}

}

@Override  public int getRowCount() {  return fichiers.length;

@Override

Page 31: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 31/35

  public int getColumnCount() {  return colonnes.length;

}

@Override  public Object getValueAt(int ligne, int colonne) {  Object[] image = (Object[]) lignes[ligne];  return image[colonne];

}

@Override  public void setValueAt(Object valeur, int ligne, int colonne) {  Object[] image = (Object[]) lignes[ligne];  if (colonne==3) {  double largeur = (Integer) image[3];  double hauteur = (Integer) image[4];  double ratio = (double)largeur / hauteur;  double récupération = (Integer) valeur;  image[4] = (int)(récupération / ratio);

tableau.repaint();}image[colonne] = valeur;

}

@Override  public Class<?> getColumnClass(int colonne) {  Object[] première = (Object[]) lignes[0];  return première[colonne].getClass();

}

@Override  public String getColumnName(int colonne) {  return colonnes[colonne];

}

@Override  public boolean isCellEditable(int ligne, int colonne) {  return colonne==0 || colonne==1 || colonne==3 ;

}

  public void changerFichier(int ligne) {  Object[] infos = (Object[]) lignes[ligne];

BufferedImage image = (BufferedImage) infos[6];  String nom = (String) infos[0];  String extension = (String) infos[1];  int largeur = (Integer) infos[3];  int hauteur = (Integer) infos[4];  double ratio = (double)largeur / image.getWidth();

BufferedImage traitement = new BufferedImage(largeur, hauteur, image.getType());  AffineTransform retailler = AffineTransform.getScaleInstance(ratio, ratio);  int interpolation = AffineTransformOp.TYPE_BICUBIC ;  AffineTransformOp retaillerImage = new AffineTransformOp(retailler, interpolation);

retaillerImage.filter(image, traitement);  try {  ImageIO.write(traitement, extension, new File("C:/Photos/" + nom + '.' + extension));  constituer();  tableau.revalidate();

}catch (IOException ex) {}

}}

 private class EditeurCellule  extends AbstractCellEditor  implements TableCellEditor {

  private JFileChooser sélecteur = new JFileChooser("C:/Photos");  private JLabel nomFichier = new JLabel();  private boolean valide;

  public EditeurCellule () {  sélecteur.addChoosableFileFilter (new FileNameExtensionFilter ("Images GIF", "gif"));  sélecteur.addChoosableFileFilter (new FileNameExtensionFilter ("Images PNG", "png"));  sélecteur.addChoosableFileFilter (new FileNameExtensionFilter ("Images JPEG", "jpg", "jpeg"));

}

@Override  public Object getCellEditorValue () {  if (valide) {  String[] découpage = sélecteur.getSelectedFile ().getName().split("\\.");

nomFichier.setText(découpage[0]);}

  return nomFichier.getText();}

@Override  public boolean shouldSelectCell (EventObject anEvent) {

valide = sélecteur.showDialog(null, "Nom du fichier")==JFileChooser.APPROVE_OPTION ;  stopCellEditing();  return true;

}

@Override  public Component getTableCellEditorComponent (JTable table, Object valeur, boolean sélectionné, int ligne, int colonne) {

nomFichier.setText((String) valeur);  return nomFichier;

}

@Override  public boolean stopCellEditing() {

nomFichier.repaint();  return super.stopCellEditing();

}

}}

 

Travailler avec les lignes et les colonnes 

Dans ce chapitre, nous allons apprendre à manipuler les lignes et les colonnes d'un tableau. Swing n'est pas du tout symétrique pour la gestion d'un tableau, c'est-à-dire 

que les opérations supportées par les lignes d'une part et par les colonnes d'autre part ne sont pas les mêmes. Le composant  JTable a été optimisé pour afficher des lignes 

Page 32: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 32/35

d'informations de même structure, comme le résultat d'une requête d'une base de données, et non pour une grille bidimensionnelle arbitraire d'objets. Nous reviendrons sur cette asymétrie au cours de ce chapitre.

Modifier la taille des colonnes

La classe TableColumn permet de contrôler la taille des colonnes. Vous pouvez ainsi choisir : 

1. void setPreferredSize( int largeur  ) : la largeur préférée de la colonne.

2. void setMinWidth( int largeur  ) : la largeur minimale de la colonne.

3. void setMaxWidth( int largeur  ) : la largeur maximale de la colonne.

Cette information est utilisée par le tableau pour la mise en forme des colonnes. Utilisez la méthode setResizable( boolean  ) pour permettre ou non à l'utilisateur de modifier la largeur d'une colonne. Vous pouvez aussi modifier la largeur d'une colonne avec la méthode setWidth( int  ) .

Lorsque la taille d'une colonne est modifiée, le comportement par défaut est de conserver la largeur totale du tableau. Naturellement, dans ce cas, les changements 

de largeur de la colonne modifiée doivent être reportés sur les autres colonnes. Avec le comportement par défaut, ces changements seront inégalement reportés sur la colonne à droite de la colonne modifiée. Cela permet à l'utilisateur d'ajuster toutes les colonnes de gauche à droite.

Vous pouvez choisir un autre comportement en utilisant la méthode setAutoResizeMode( int  ) : 

1. JTable.AUTO_RESIZE_OFF : Ne modifie pas les colonnes, change la taille du tableau.

2. JTable.RESIZE_NEXT_COLUMN : Modifie uniquement la taille de la colonne suivante.

3. JTable.RESIZE_SUBSEQUENT_COLUMN : Modifie identiquement toutes les colonnes restantes. C'est le comportement par défaut .

4. JTable.AUTO_RESIZE_LAST_COLUMN : Modifie uniquement la taille de la dernière colonne.

5. JTable.AUTO_RESIZE_ALL_COLUMN : Modifie toutes les colonnes du tableau. Ce choix est à éviter parce qu'il devient très difficile d'ajuster la largeur de 

plusieurs colonnes.

Sélectionner des lignes, des colonnes et des cellulesEn fonction du mode de sélection, l'utilisateur peut sélectionner des lignes, des colonnes ou des cellules isolées du tableau.

Par défaut, la sélection de lignes est autorisée. Une ligne entière est sélectionnée lorsque l'utilisateur clique sur une cellule. Appelez la méthode 

setRowSelectionAllowed( false  ) de la classe JTable pour supprimer cette sélection par lignes.

Lorsque la sélection par lignes est permise, vous pouvez choisir parmi plusieurs modes de sélection. Vous devrez donc récupérer le modèle de sélection et utiliser sa méthode setSelectionMode( int  ) : 

table. getSelectionMode(). setSelectionMode( mode )

Ici mode peut prendre l'une des trois valeurs suivantes : 

1. ListSelectionModel.SINGLE_SELECTION  : une seule ligne.

2. ListSelectionModel.SINGLE_INTERVAL_SELECTION : un ensemble continu de lignes.

3. ListSelectionModel.MULTIPLE_INTERVAL_SELECTION : n'importe quelles lignes.

La sélection des colonnes est désactivée par défaut. Elle peut être activée avec l'appel de la méthode  setColumnSelectionAllowed( true  ) de la classe JTable .

L'activation des deux types de sélection (lignes et colonnes) équivaut à activer la sélection de cellules. L'utilisateur sélectionne alors des plages de cellules. Vous 

pouvez également activer ce réglage par l'appel de la méthode setCellSelectionEnabled( true  ) de la classe JTable .

Page 33: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 33/35

Vous pouvez identifier les lignes et les colonnes sélectionnées en appelant les méthodes  getSelectedRows() et getSelectedColumns()  de la classe JTable . Ces deux 

méthodes renvoient un tableau d'indices int[] correspondant aux éléments sélectionnées.

Cacher et afficher des colonnes

La méthode  removeColumn() de la classe JTable supprime une colonne de l'affichage d'un tableau. Les données de la colonne ne sont en fait pas supprimées du modèle,

elles sont justes cachées. La méthode removeColumn() prend un argument de type TableColumn . Si vous possédez un numéro de colonne, par exemple à partir d'un appel à getSelectedColumns() , vous devrez encore demander au modèle de l'arbre la colonne réelle de l'arbre qui correspond à ce numéro : 

JTable table = new JTable();TableColumnModel  modèleColonne = table.getColumnModel();TableColumn colonne = modèleColonne.getColumn(i);table.removeColumn(colonne);

Si vous avez mémorisé ce numéro de colonne, il est possible de l'afficher à nouveau : 

table.addColumn( colonne ) ;

Cette méthode ajoute la colonne à la fin du tableau. Si vous préférez qu'elle apparaisse à un autre endroit, vous devez appeler la méthode  moveColumn() .

Vous pouvez également ajouter une nouvelle colonne qui correspond à un indice de colonne du modèle de tableau, en ajoutant un nouvel objet  TableColumn : 

table.addColumn( new TableColumn( indexmodèleColonne ) ) ;

En fait, plusieurs colonnes du tableau peuvent être affichées à partir d'une même colonne du modèle.§ 

Cependant, il n'existe pas de méthode dans  JTable pour cacher ou afficher des lignes. Si vous souhaitez cacher une ligne, vous devrez avoir recours à un 

modèle de filtre.

Ajouter et supprimer des lignes dans le modèle de tableau par défautLa classe DefaultTableModel  est une classe concrète qui implémente l'interface TableModel . Elle stocke une grille bidimensionnelle d'objets. Si vos données se trouvent déjà sous cette forme, il n'est pas nécessaire de recopier toutes ces données dans un modèle de tableau par défaut. En revanche, si vous possédez peu de données, il peut 

être pratique de les copier pour obtenir rapidement un tableau. La classe DefaultTableModel possède des méthodes permettant d'ajouter des lignes et des colonnes et de 

supprimer des lignes.

Les méthodes addRow()  et addColumn()  de la classe DefaultTableModel ajoutent une nouvelle ligne ou une nouvelle colonne dans les données. Elles nécessitent un 

tableau Object[]  ou un vecteur qui contient les nouvelles données. Avec la méthode  addColumn() , vous devez aussi fournir le nom de la nouvelle colonne. Ces méthodes ajoutent les nouvelles données à la fin de la grille. Pour insérer une nouvelle ligne au milieu des données existantes, utilisez la méthode  insertRow() .

Inversement la méthode removeRow() supprime une ligne du modèle.

Il n'existe aucune méthode pour insérer une colonne au milieu d'une grille. Il n'existe également aucune méthode pour supprimer une colonne.§ 

Comme l'objet JTable s'enregistre lui-même comme un écouteur de modèle de tableau, le modèle avertit le tableau lorsque les données sont insérées ou supprimées.

Pour l'instant, le tableau met à jour l'affichage.

Exemple de mise en oeuvre

Le programme suivant montre comment fonctionnent la sélection et la modification. Un modèle de tableau par défaut contient un simple ensemble de données, comme une 

table de multiplication. Le menu Edition contient les commandes suivantes : 

1. Cacher toutes les colonnes sélectionnées ; 

2. Afficher toutes les colonnes cachées ; 

3. Ajouter une ligne de données à la fin du modèle ; 

4. Supprimer du modèle les lignes sélectionnées ; 

Page 34: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 34/35

5. Effacer les cellules sélectionnées.

codage correspondant 

package tables;

import  java.awt.event.*;import  java.util.ArrayList;import  javax.swing.*;import  javax.swing.table.*;

public class Tables extends JFrame {  private DefaultTableModel  modèle = new DefaultTableModel(10, 10);  private JTable table = new JTable(modèle);  private final ArrayList<TableColumn> colonnesSupprimées = new ArrayList<TableColumn>();  private JMenuBar menu = new JMenuBar();  private JMenu sélection = new JMenu("Sélection");  private JMenu édition = new JMenu("Edition");  private JCheckBoxMenuItem lignes = new JCheckBoxMenuItem("Lignes", table.getRowSelectionAllowed ());  private JCheckBoxMenuItem colonnes = new JCheckBoxMenuItem("Colonnes", table.getColumnSelectionAllowed ());

  private JCheckBoxMenuItem cellules = new JCheckBoxMenuItem("Cellules", table.getCellSelectionEnabled ()); 

public Tables() {  super("Sélections dans une table");  for (int i=0; i<modèle.getRowCount(); i++)  for (int  j=0; j<modèle.getColumnCount(); j++)  modèle.setValueAt((i+1)*( j+1), i, j);  add(new JScrollPane(table));  setJMenuBar(menu);

menu.add(sélection);menu.add(édition);sélection.add(lignes);sélection.add(colonnes);sélection.add(cellules);lignes.addActionListener(new ActionListener() {

  public void actionPerformed(ActionEvent e) {  table.clearSelection ();  table.setRowSelectionAllowed (lignes.isSelected());

cellules.setSelected(table.getCellSelectionEnabled ());}

});colonnes.addActionListener (new ActionListener() {

  public void actionPerformed(ActionEvent e) {  table.clearSelection ();  table.setColumnSelectionAllowed (colonnes.isSelected());

cellules.setSelected(table.getCellSelectionEnabled ());}

});cellules.addActionListener (new ActionListener() {

  public void actionPerformed(ActionEvent e) {  table.clearSelection ();  table.setCellSelectionEnabled (cellules.isSelected());

lignes.setSelected(table.getRowSelectionAllowed ());colonnes.setSelected(table.getColumnSelectionAllowed ());

}});édition.add("Cacher les colonnes").addActionListener (new ActionListener() {

  public void actionPerformed(ActionEvent e) {  int[] sélectionnées = table.getSelectedColumns ();  TableColumnModel  modèleColonne = table.getColumnModel();

  for (int i=sélectionnées.length-1; i>=0; i--) {  TableColumn colonne = modèleColonne.getColumn(sélectionnées[i]);  table.removeColumn(colonne);  colonnesSupprimées.add(colonne);

}}

});édition.add("Afficher les colonnes").addActionListener (new ActionListener() {

  public void actionPerformed(ActionEvent e) {  for (TableColumn colonne : colonnesSupprimées) table.addColumn(colonne);  colonnesSupprimées.clear();

}});édition.add("Ajouter une ligne").addActionListener(new ActionListener() {

  public void actionPerformed(ActionEvent e) {  Integer[] nouvellesCellules = new Integer[modèle.getColumnCount()];  for (int i=0; i<nouvellesCellules.length; i++)

nouvellesCellules[ i] = new Integer((i+1) * modèle.getRowCount()+1);  modèle.addRow(nouvellesCellules);

}

});édition.add("Supprimer des lignes").addActionListener(new ActionListener() {  public void actionPerformed(ActionEvent e) {  int[] sélectionnées = table.getSelectedRows();  for (int i=sélectionnées.length-1; i>=0; i--) modèle.removeRow(sélectionnées[i]);

}});édition.add("Effacer les cellules").addActionListener(new ActionListener() {

  public void actionPerformed(ActionEvent e) {  for (int i=0; i<table.getRowCount(); i++)  for (int  j=0; j<table.getColumnCount(); j++)  if (table.isCellSelected(i, j)) table.setValueAt(0, i, j);

}});

Page 35: Arbres Et Tableaux

5/13/2018 Arbres Et Tableaux - slidepdf.com

http://slidepdf.com/reader/full/arbres-et-tableaux 35/35

  pack();  setDefaultCloseOperation (EXIT_ON_CLOSE);  setVisible(true);

public static void main(String[] args) { new Tables(); }}