précédent | suivant | table des matières

JTree

Sommaire
  1. Le tutoriel de Sun.
  2. Généralités.
  3. JTree.
  4. DefaultTreeModel.
  5. DefaultMutableTreeNode.
  6. Réponse à la sélection d’un nœud dans un JTree.
  7. Personnalisation des icônes.
  8. Exemples : 
    1. Visualisation de la hiérarchie de fichiers.
    2. Visualisation d’expressions arithmétiques.
    3. Visualisation d’un arbre n-aire.
hiérarchie

(Démonstration)

Le composant JTree permet de visualiser une structure d'arbre.

1Généralités

Une instance de JTree ne contient pas de données, mais est simplement une vue de données contenues dans son modèle (architecture MVC).

Les données sont affichées ligne par ligne, chaque ligne contenant exactement une donnée qui est un Nœud

Un arbre a une racine

Les nœuds qui n’ont pas de fils sont des feuilles.

Une instance de JTree peut être créée par un constructeur par défaut, qui affiche l’image ci-contre, ou par un constructeur ayant en paramètre un TreeNode ou un TreeModel.

exemple de jTree

TreeModel est une interface, qui est implantée par exemple par DefaultTreeModel.

TreeNode est une interface, qui est dérivée en MutableTreeNode, qui est implantée par exemple par DefaultMutableTreeNode.

Exemples : 

Le code suivant permet d’afficher un arbre constitué d'une racine :
DefaultMutableTreeNode racine1 =
       new DefaultMutableTreeNode("c’est la racine") ;
JTree monArbre1 = new JTree(racine1) ; 
Si nous voulons ajouter 3 enfants à la racine nous écrirons : 
DefaultMutableTreeNode racine2 =
       new DefaultMutableTreeNode("c’est la racine");
for( int i = 0; i < 3; ++i)
   racine2.add( new DefaultMutableTreeNode("fils N° "+ i));
JTree monArbre2 = new JTree(racine2) ;
Pour ajouter des nœuds dans l’arbre pendant l’exécution d’un programme sans avoir à reconstruire l’arbre, nous devons passer par un TreeModel, interface qui est implantée par DefaultTreeModel : 
racine3 = new DefaultMutableTreeNode("c’est la racine");
DefaultTreeModel arbreModele = new DefaultTreeModel(racine3);
monArbre3 = new JTree(arbreModele); 
   ...
for( int i = 0; i < 3; ++i){
    DefaultMutableTreeNode x = 
	  new DefaultMutableTreeNode("fils N° "+ i);
    for( int j = 0; j < 2*i; ++j)
    x.add( new DefaultMutableTreeNode("filsN° "+ i+"  "+j));
    racine3.add(x);
}

2JTree

Constructeurs

JTree()
Crée un JTree avec l'exemple par défaut
JTree(Hashtable<?,?> valeur) 
Crée un JTree sans racine, tous les éléments de la Hashtable sont des feuilles.
JTree(Object[] valeur)
Crée un JTree sans racine, tous les éléments  du tableau sont des feuilles.
JTree(TreeModel modele)
Crée un JTree avec son modèle.
JTree(TreeNode racine)
Crée un JTree avec un nœud  racine.Un nœud  feuille est un nœud  qui n'a pas de nœud  fils.
JTree(TreeNode racine, boolean b)
Crée un JTree avec un nœud  racine.
  • b vaut true : les nœuds qui n'autorisent pas les nœuds fils sont des feuilles.
  • b vaut false : les nœuds sans nœud  fils sont des feuilles.
JTree(Vector<?> valeur)
Crée un JTree sans racine, tous les éléments du Vector sont des feuilles.

Quelque méthodes

Les branches de l'arbre peuvent être déployée ou refermées :

void collapseRow(TreePath chemin)
Referme le sous arbre désigné par le chemin chemin.
voidcollapseRow(int i)
Referme le sous-arbre dépendant de la ligne i.
void expandPath(TreePath chemin)
Déploie le sous-arbre désigné par chemin.
void expandRow(int i)
Déploie le sous arbre dependant de la ligne i.
boolean isCollapsed(int i)
Retourne true si le sous-arbre correspondant à la ligne i est refermé.
boolean isCollapsed(TreePath chemin)
Retourne true si le sous-arbre correspondant au chemin est refermé.
boolean isExpanded(int i)
Retourne true si le sous-arbre correspondant à la ligne i est déployé.
boolean isExpanded(TreePath chemin)
Retourne true si le sous-arbre correspondant au chemin est déployé.
TreeModel getModel()
Retourne le TreeModel qui contient les données.
voidsetModel(TreeModel modele)
Change le TreeModel du JTree.

Affecter ou récupérer le modèle du JTree.

TreeModel getModel()
Retourne le TreeModel qui contient les données.
void setModel(TreeModel modele)
Change le TreeModel du JTree.

Visibilité de la racine, et de lignes.

void setRootVisible(boolean b)
Rend la racine visible ou invisible.
boolean isRootVisible()
Retourne true si la racine est visisble.
int getRowCount()
Retourne le nombre de lignes affichées.
void setVisibleRowCount(int n)
Le nombre de lignes visible est n.

3DefaultTreeModel

La classe DefaultTreeModel définit un modèle de données pour la classe JTree. Les modifications dans le modèle sont répercutées dans le JTree.

Constructeurs

DefaultTreeModel(TreeNode racine) 
Crée un DefaultTreeModel ayant racine pour racine.
DefaultTreeModel(TreeNode racine, boolean b)
Crée un DefaultTreeModel ayant racine pour racine.
  • b vaut true : les nœuds qui n'autorisent pas les nœuds fils sont des feuilles.
  • b vaut false : les nœuds sans nœud  fils sont des feuilles.

Quelques méthodes

void setRoot(TreeNode racine)
Modifie la racine.
Object getRoot() 
Retourne le nœud  racine du DefaultTreeModel.
void reload()
Cette méthode doit être appelée chaque fois que le modèle a été modifié, pour que l’affichage tienne compte du changement.
void insertNodeInto(MutableTreeNode fils,
   MutableTreeNode parent, int i)
Insère fils au rang i dans la liste des fils du nœud  parent.
void removeNodeFromParent(MutableTreeNode n)
Enlève le nœud  n du modèle.
Object getChild(Object parent, int index)
Retourne le fils de rang i du nœud parent.
int getChildCount(Object parent)
Retourne le nombre de fils du nœud parent.
int getIndexOfChild(Object parent, Object fils)
Retourne le rang du nœud  fils de père parent.
boolean isLeaf( Object n)
Retourne true si n est une feuille.

Exemple :

DefaultTreeModel dtm;
JTree jt;
TreePath tp;
dtm = new DefaultTreeModel(
      new DefaultMutableTreeNode("racine"));
jt = new JTree(dtm);
Enlever tous les nœuds
DefaultMutableTreeNode racine =
        (DefaultMutableTreeNode)dtm.getRoot();
racine.removeAllChildren();
dtm.reload();
Ajouter un nœud  fils au nœud sélectionné :
TreePath tp = jt.getSelectionPath();
if(tp != null){
   DefaultMutableTreeNode parent = 
       (DefaultMutableTreeNode)
       tp.getLastPathComponent());
   DefaultMutableTreeNode nouveau = 
         new DefaultMutableTreeNode("nœud  "+(++in));
   dtm.insertNodeInto(nouveau, 
         parent, parent.getChildCount());
   jt.scrollPathToVisible(
      new TreePath(nouveau.getPath()));
}
Enlever le nœud sélectionné
TreePath tp = jt.getSelectionPath();
if(tp != null){
   DefaultMutableTreeNode noeud = 
      (DefaultMutableTreeNode)
	  (tp.getLastPathComponent());
   if(!noeud.isRoot())dtm.removeNodeFromParent(noeud);
}

4DefaultMutableTreeNode

Un defaultMutableTreeNode est un nœud à tout faire. Un nœud a au plus un parent et 0 ou plus nœuds fils. Un defaultMutableTreeNode offre des méthodes permettant d’examiner, ou de modifier le parent ou les nœuds enfants. Un nœud sans parent est la racine. Un nœud sans enfant est une feuille. Un defaultMutableTreeNode a parmi ses attributs un attribut parent de type defaultMutableTreeNode, et un attribut childrensde type Vector.

Constructeurs :

DefaultMutableTreeNode()
Crée un DefaultMutableTreeNode sans parent et sans enfant, qui peut avoir des enfants.
DefaultMutableTreeNode(Object o)
Crée un DefaultMutableTreeNode sans parent et sans enfant, qui peut avoir des enfants. Ce nœud  est initialisé avec o.
DefaultMutableTreeNode(Objecto, boolean b)
Crée un DefaultMutableTreeNode sans parent et sans enfant.Ce nœud  est initialisé avec o.
  • b vaut true : le nœud  peut avoir des enfants.
  • b vaut false : le nœud  ne peut pas avoir d'enfants.

Ajout ou retrait de nœuds :

void add(MutableTreeNode fils)
fils devient un nœud enfant du nœud .
void insert(MutableTreeNode f, int i)
Insère f dans la liste des fils de ce nœud , à l’indice i.
void remove(int i)
Enlève le nœud à l’indice i.
void removeFromParent()
Enlève ce nœud de la liste des enfants de son parent.
void removeAllChildren()
Enlève tous les enfants du nœud.
void remove(MutableTreeNode f)
Enlève f de la liste des enfant de ce nœud.
void setParent(MutableTreeNode n)
Change le parent du nœud. Le nœud n’est pas ajouté à la liste des enfants de son nouveau parent.

Informations sur le nœud :

boolean isRoot()
Retourne true si le nœud est la racine.
TreeNode getRoot()
Retourne la racine.
boolean isLeaf()
Retourne true si le nœud est une feuille.
int getDepth()
Retourne la distance de ce nœud à la racine.
int getChildCount()
Retourne le nombre de nœuds enfants de ce nœud.
int getLeafCount()
Retourne le nombre de feuilles du sous-arbre dépendant de ce nœud.
nt getSiblingCount()
Retourne le nombre de frères du nœud (y compris lui même)
TreeNode getParent()
Retourne le nœud parent.
TreeNode[]getPath()
Retourne l’ensemble des nœuds sur le chemin de la racine à ce nœud.
TreeNode getChildAfter(TreeNode fils)
Retourne le nœud enfant suivant fils.
TreeNode getChildAt(int i)
Retourne le nœud enfant à l’indice i
TreeNode getChildBefore(TreeNode fils)
Retourne le nœud enfant qui suit fils.
TreeNode getFirstChild()
Retourne le premier fils de ce nœud.
int getIndex(TreeNode fils)
Retourne l’indice de fils dans le vecteur des enfants.
TreeNode getLastChild()
Retourne le dernier fils.
DefaultMutableTreeNode getNextNode()
Retourne le nœud qui suit dans un parcours préfixé.
DefaultMutableTreeNode getPreviousNode()
Retourne le nœud qui précède.

Les ascendants, descendants et frères :

DefaultMutableTreeNode getNextSibling()
Retourne le frère suivant du nœud .
DefaultMutableTreeNode getPreviousSibling()
Retourne le frère précédent du nœud.
boolean isNodeSibling(TreeNode n)
Retourne true si n est un frère du nœud.
TreeNode getChildAfter(TreeNode fils)
Retourne le nœud enfant suivant fils.
TreeNode getChildAt(int i)
Retourne le nœud enfant à l’indice i.
TreeNode getChildBefore(TreeNode fils)
Retourne le nœud enfant qui suit fils.
TreeNode getFirstChild()
Retourne le premier fils de ce nœud.
int getIndex(TreeNode fils)
Retourne l’indice de fils dans le vecteur des enfants.
TreeNode getLastChild()
Retourne le dernier fils.
DefaultMutableTreeNode getNextNode()
Retourne le nœud qui suit dans un parcours préfixé.
DefaultMutableTreeNode getPreviousNode()
Retourne le nœud qui précède.
TreeNode getSharedAncestor(DefaultMutableTreeNode n)
Retourne l’ancêtre commun le plus proche entre ce nœud et n.
boolean isNodeAncestor(TreeNode n)
Retourne true si le nœud n est un ancêtre de ce nœud.
boolean isNodeChild(TreeNode n)
Retourne true si le nœud n est un enfant de ce nœud.
boolean isNodeDescendant(DefaultMutableTreeNode n)
Retourne true si le nœud n est un descendant de ce nœud.

Les feuilles :

DefaultMutableTreeNode getFirstLeaf()
Retourne la première feuille du sous-arbre dépendant de ce nœud.
DefaultMutableTreeNode getLastLeaf()
Retourne la dernière feuille du sous-arbre dépendant de ce nœud.
DefaultMutableTreeNode getNextLeaf()
Retourne la première feuille suivant ce nœud, ou null si ce nœud est la dernière feuille.
DefaultMutableTreeNode getPreviousLeaf()
Retourne la feuille qui précède ou null si ce nœud est la première feuille.

5Réponse à la sélection d'un nœud  dans un JTree

Pour exécuter une action lorsqu’un nœud est sélectionné :

Exemple : affichage dans statusBar du chemin du nœud sélectionné à la racine :

monArbre.addTreeSelectionListener( new TreeSelectionListener(){
   public void valueChanged(TreeSelectionEvent e){
      DefaultMutableTreeNode lenoeud = 
	     (DefaultMutableTreeNode)monArbre.getLastSelectedPathComponent();
      // pour afficher le chemin de 
      // ce noeud vers la racine de l’arbre
      String s = lenoeud .toString();
      while(!lenoeud.isRoot()) {
          lenoeud = (DefaultMutableTreeNode)
          lenoeud.getParent();
          s = lenoeud .toString() + s;
      }
      statusBar.setText(s);
   }
});

personnaliser

6Personnalisation des icônes

On peut modifier les différentes icônes de la façon suivante :

JTree monArbre ...
DefaultTreeCellRenderer renderer = 
      new DefaultTreeCellRenderer();
renderer.setOpenIcon(icône pour un sous-arbre déployé);
renderer.setClosedIcon(cône pour un sous-arbre fermé);
renderer.setLeafIcon(icône de feuille);
monArbre.setCellRenderer(renderer);

7Exemples

71Visualiser la hiérarchie de fichiers

DefaultMutableTreeNode racine = 
         new DefaultMutableTreeNode("");DefaultTreeModel tm ;

JTree  monArbreF ;
// construction de la liste des lecteurs
File[] lecteurs = File.listRoots();
for (int i = 0; i < lecteurs.length; ++i){
   DefaultMutableTreeNode nouveau = 
       new DefaultMutableTreeNode(lecteurs[i].toString());
   racine.add(nouveau);
}
tm = new DefaultTreeModel(racine);
monArbreF = new JTree(tm);
// modification du rendu de l’arbre
DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
renderer.setOpenIcon(
      new ImageIcon(getClass().getResource("/jtree/ouvert.gif")));
renderer.setClosedIcon(
      new ImageIcon(getClass().getResource("/jtree/ferme.gif")));
renderer.setLeafIcon(null);
renderer.setBorderSelectionColor(Color.RED);
renderer.setBackgroundSelectionColor(Color.WHITE);
renderer.setTextSelectionColor(Color.RED);
renderer.setFont(new Font("Comic sans ms",Font.BOLD, 14));
renderer.setTextNonSelectionColor(Color.BLUE);
monArbreF.setCellRenderer(renderer);
// déploiement du sous arbre d’un nœud sélectionné
monArbreF.addTreeSelectionListener( new TreeSelectionListener(){
   public void valueChanged(TreeSelectionEvent e) {
      DefaultMutableTreeNode lenoeud = 
        (DefaultMutableTreeNode)monArbreF.getLastSelectedPathComponent();
      if (lenoeud  == null)   
      return;
      developper(lenoeud );
   }
});
void developper(DefaultMutableTreeNode n) {
// construction du chemin depuis la racine
TreeNode[] paths = n.getPath();
String path = "";
for (int i = 0; i < paths.length; ++i)<
   if (paths[i].toString() != null)
      path += "\\" + paths[i];
   File f = new File(path.substring(1));
   // si le nœud  est un repertoire
   // contenant au moins un fichier
   // créer la liste des fichiers du répertoire
   if (f.isDirectory() && n.getChildCount() == 0) {
      File tf[] = f.listFiles();
      for (int i = 0; i < tf.length; ++i) {
          tm.insertNodeInto(
             new DefaultMutableTreeNode(tf[i].getName()), n, i);
          monArbreF.expandPath(
             new TreePath(paths));
      }
   }
}

72Visualiser des expressions arithmétiques

exp arith

Pour obtenir l'arbre ci-contre, nous définissons les 3 classes suivantes :

abstract public class Noeud extends DefaultMutableTreeNode {
   boolean deploye = false;
   public Noeud() {
      children = new Vector();
   }
   public void setExpanded(boolean b) {
      deploye = b;
   }
}
public class NoeudOperateur extends Noeud {
   public static final String MULTIPLICATION = "*";
    public static final String DIVISION = "/";
   public static final String ADDITION = "+";
   public static final String SOUSTRACTION = "-";
   String operateur;
   public NoeudOperateur() {
      this(ADDITION);
   }
   public NoeudOperateur(String operateur) {
      this.operateur = operateur;
      deploye =       false;
   }
public String getOperateur() { return operateur; } public void setOperateur(Object o) { if (o instanceof String ) if(estOperateur((String)o)) this.operateur = (String) o; else throw new IllegalArgumentException( "l'opérateur doit +, -, / ou * : "+o); else throw new IllegalArgumentException( "l'opérateur doit être une chaîne"+ " de caractères : " + o + "  "); } boolean estOperateur(String s){ return s.equals(NoeudOperateur.ADDITION)|| s.equals(NoeudOperateur.MULTIPLICATION)|| s.equals(NoeudOperateur.DIVISION)|| s.equals(NoeudOperateur.SOUSTRACTION); } public String toString() { ... } }
public class NoeudEntier extends Noeud{
    Integer entier;
    public NoeudEntier(int i) {
       entier = new Integer(i);
    }
    public NoeudEntier(Integer i) {
       entier =i;
    }
    public String toString(){
       return entier.toString();
    }
    public void setVal(int v){
       entier = new Integer(v);
    }
 }

On peut alors construire un arbre :

DefaultMutableTreeNode rac = new DefaultMutableTreeNode();
NoeudOperateur racine = new NoeudOperateur(NoeudOperateur.ADDITION);
NoeudOperateur ng1 = new NoeudOperateur(NoeudOperateur.MULTIPLICATION);
NoeudOperateur ng2 = new NoeudOperateur(NoeudOperateur.SOUSTRACTION);
NoeudOperateur ng3 = new NoeudOperateur(NoeudOperateur.SOUSTRACTION);

ExpressionTreeModel leModele = new ExpressionTreeModel(rac);
leModele.insertNodeInto(new NoeudEntier(1), ng1, 0);
leModele.insertNodeInto(new NoeudEntier(2), ng1, 1);
leModele.insertNodeInto( ng3, ng2, 0);
leModele.insertNodeInto( new NoeudEntier(4), ng2, 1);
leModele.insertNodeInto( new NoeudEntier(3), ng3, 0);
leModele.insertNodeInto( new NoeudEntier(5), ng3, 1);
leModele.insertNodeInto( ng1, racine, 0);
leModele.insertNodeInto( ng2, racine, 1);
leModele.insertNodeInto( ng3, ng2, 1);
leModele.insertNodeInto( racine, rac, 0);
JTree monArbre = new JTree(leModele);
monArbre.addTreeExpansionListener(new javax.swing.event.TreeExpansionListener(){
   public void treeExpanded(javax.swing.event.TreeExpansionEvent e){
      NoeudOperateur no = (NoeudOperateur);e.getPath().getLastPathComponent();
      no.setExpanded(true);
   }
   public void treeCollapsed(javax.swing.event.TreeExpansionEvent e) {
      NoeudOperateur no = (NoeudOperateur )e.getPath().getLastPathComponent();
      no.setExpanded(false);
   }
});
public class ExpressionTreeModel extends DefaultTreeModel {
   public ExpressionTreeModel(DefaultMutableTreeNode racine) {
      super(racine);
   }
   public void valueForPathChanged(TreePath chemin, Object nVal){
      Object[] p = chemin.getPath();
      Object n = p[p.length-1];
      if(p.length==1){// édition de la racine
          ((NoeudOperateur)n).setOperateur(nVal);
      }else{
         NoeudOperateur parent = (NoeudOperateur)p[p.length-2];
         if(n instanceof NoeudOperateur)// c'est un opérateur
           ((NoeudOperateur) n).setOperateur(nVal);
         else//c'est un entier
           ((NoeudEntier)n).setVal(Integer.parseInt((String)nVal));
      }
      this.nodeChanged((Noeud)n);
   }
}

73Visualiser des arbres n-aires

Nous voulons faire une représentation d’un arbre nommé lArbre constitué de Nœuds dans un JTree dont le modèle s’appelle leModele. Un Nœud à trois attributs Info, filsAine et frere.

private void enArbre( DefaultMutableTreeNode parent, Noeud r) { 
   if (r != null) {
      DefaultMutableTreeNode fils = new DefaultMutableTreeNode(r.getInfo());
      parent.add(fils);
      enArbre(fils, r.getFilsAine());
      enArbre( parent, r.getFrere());
   }
}

Avec un appel initial :

enArbre(leModele.getRoot(), lArbre.getRacine());

Pour initialiser le modèle du JTree, il suffit d'enlever tous les fils de la racine :

((DefaultMutableTreeNode)leModele.getRoot()).removeAllChildren();

haut de la page