Utilizzo di javax.swing.JTree

In questo tutorial viene illustrato l’utilizzo del componente Swing JTree.

Il modo migliore per visualizzare una struttura dati gerarchica (come può essere un file xml) è usare un albero. Il componente Swing che lo implementa è javax.swing.JTree, supportato dalle classi contenute nel package javax.swing.tree per quanto riguarda la manipolazione del contenuto e da alcune classi del package javax.swing.event per la gestione degli eventi.

 

Costruttori e prime personalizzazioni

La classe JTree fornisce diversi costruttori, un paio sono:

Il costruttore senza parametri crea un albero con un contenuto predefinito (cioè ha già inseriti alcuni nodi e sotto-nodi). Il secondo costruttore crea un albero a cui viene associato il modello indicato come parametro mentre il terzo costruttore prende in ingresso il nodo che sarà poi la radice dell’albero.

A meno di esigenze particolari, è meglio creare un albero con solo la radice e caricare i sotto-nodi in un secondo momento. Dopo aver istanziato l’albero conviene dargli qualche ritoccatina, a seconda delle nostre esigenze. Diversi sono i metodi di JTree utili a queste “ritoccatine”. Alcuni sono:

  • setEditable() stabilisce se l’utente può modificare o meno il nome dei nodi dopo che questi sono stati selezionati col doppio click. Se si passa true l’utente può modificare, altrimenti no;
  • setRootVisible() stabilisce invece se il nodo radice è visibile o meno. Se si passa true il nodo radice sarà visibile come tutti gli altri nodi, altrimenti i primi nodi ad essere visibili saranno i figli della radice;
  • setShowsRootHandles() stabilisce se la “maniglia” del nodo radice deve essere visualizzata (true) o meno (false);
  • setVisibleRowCount() imposta il numero di righe che l’albero mostrerà prima di far apparire le barre di scorrimento (sempre che le mettiate, le barre);
  • setModel() permette di associare all’albero un modello (eventualmente) personalizzato;

 

Inserire nodi

Ogni nodo di un JTree è un oggetto della classe DefaultMutableTreeNode (contenuta in javax.swing.tree). La classe DefaultMutableTreeNode implementa MutableTreeNode (che a sua volta implementa TreeNode) quindi gli oggetti di questa classe possono essere passati al costruttore di JTree senza problemi. Un primo nodo viene inserito nell’albero quando questo viene istanziato:

N.B.: il nodo radice può essere creato direttamente nella chiamata del costruttore di JTree. Conviene però tenere un riferimento alla radice poichè per alcune operazioni il nodo radice è indispensabile. In ogni caso lo si può ottenere in qualunque momento chiamando il metodo getRoot() del modello associato all’albero.

Per aggiungere nodi ad un albero ci sono due metodi: uno utilizza il modello associato all’albero mentre l’altro utilizza direttamente i nodi.

1) Utilizzando il modello associato all’albero
Innanzitutto è necessario ottenere un riferimento al modello:

Dopo aver ottenuto il modello, usarlo per chiamare il metodo insertNodeInto con i giusti parametri (nell’ordine: il nodo da aggiungere, il nodo a cui aggiungere il sotto-nodo e l’indice in cui inserire il nodo):

Nell’esempio qui sopra creo due nodi. Il primo viene agganciato alla radice mentre l’altro viene agganciato al primo nodo. Per ottenere l’indice chiamare il metodo getChildCount: ogni nodo memorizza i propri sotto-nodi di primo livello in un oggetto Vector e getChildCount restituisce l’indice del primo elemento del vettore che può memorizzare il nuovo nodo.

2) Utilizzando direttamente i nodi
Per aggiungere un sotto-nodo ad un nodo, chiamare il metodo add sul nodo a cui agganciare il nuovo nodo, passando come parametro il nodo da agganciare.

In entrambi i casi, per rendere visibili le modifiche fatte occorre chiamare sul modello il metodo reload:

reload ricarica tutto l’albero mentre reload(nodoDaRicaricare) ricarica solo il nodo indicato (ed è particolarmente utile quando si rinomina un nodo).  Se si utilizza reload tutti i nodi aperti vengono immediatamente chiusi. Se si vuole mostrare un particolare nodo (magari l’ultimo inserito), occorre chiamare il metodo scrollPathToVisible sull’istanza di JTree, indicando il percorso (come oggetto TreePath) del nodo da rendere visibile:

se il JTree contiene molti nodi e utilizza delle barre di scorrimento, scrollPathToVisible non “scrolla” lo ScrollPane. Se si vuole che il nodo venga portato “a vista”, occorre chiamare setSelectionPath dopo scrollPathToVisible:

 

Rinominare un nodo

Per rinominare un nodo, è sufficiente chiamare il metodo setUserObject sul nodo da rinominare, passando come parametro il nuovo nome:

se il nuovo nome occupa più spazio (in pixel) di quello vecchio, sull’etichetta associata al nome appariranno una serie di puntini (per indicare, appunto, che il nuovo nome è troppo lungo). Per far sparire i puntini è necessario aprire e chiudere il nodo (in questo modo, quando verrà riaperto le dimensioni dell’etichetta verranno ricalcolate) oppure, molto semplicemente, chiamare il metodo reload passando come parametro il nodo rinominato:

 

Rimuovere un nodo

Per rimuovere un nodo si utilizzano diversi metodi, sia usando il modello, sia usando direttamente i nodi:

  • removeNodeFromParent va invocato sul modello e rimuove dall’albero il nodo indicato come parametro;
  • removeAllChildren rimuove tutti i sotto-nodi del nodo che lo chiama;
  • remove rimuove dal nodo chiamante il nodo indicato tra le parentesi;
  • removeFromParent rimuove dall’albero il nodo che lo invoca;

 

Esplorare l’albero

Ogni nodo possiede diversi metodi per ottenere altri nodi:

  • getChildAfter restituisce il sotto-nodo che segue il sotto-nodo passato come parametro;
  • getChildAt restituisce l’i-esimo sotto-nodo del nodo che lo invoca;
  • getChildBefore restituisce il sotto-nodo che precede il sotto-nodo passato come parametro;
  • getParent restituisce il nodo genitore del nodo chiamante;
  • children restituisce tutti i sotto-nodi di primo livello del nodo chiamante all’interno di una enumerazione.

 

Per ottenere i singoli nodi dovrete scorrere nodo per nodo l’enumerazione, come se fosse un iteratore:

 

Rilevare quando l’utente seleziona un nodo dell’albero

Quando l’utente seleziona un qualsiasi nodo dell’albero viene generato un TreeSelectionEvent (classe contenuta nel package javax.swing.event). Per rilevare un TreeSelectionEvent bisogna predisporre l’ascoltatore adeguato (un TreeSelectionListener, sempre nel package javax.swing.event), attraverso, ad esempio, una classe interna anonima:

non è ovviamente obbligatorio usare una classe interna anonima, volendo si può anche creare la classe listener in un file a parte, ma visto l’utilizzo limitato che ha, la soluzione con classe interna anonima è senza dubbio la migliore (a meno di esigenze particolari).

 

Altri metodi utili

Quando si lavora con i nodi, magari può essere utile sapere a che distanza si trova un particolare nodo dalla radice. A questo scopo esiste il metodo getLevel, che restituisce appunto la distanza del nodo che lo invoca dalla radice:

Altre funzionalità sono mostrate nell’esempio completo più sotto. Questo esempio riunisce gran parte di ciò che ho mostrato in questo articolo, introducendo anche alcune cosette nuove. Il programma è composto da una JDialog su cui si trova un JTree e due bottoni, uno per confermare la scelta, l’altro per terminare. Nel JTree viene caricato il contenuto di cartelle e sottocartelle della directory corrente.

 

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *