2009-12-16 5 views
13

Prima di tutto, lasciatemi dire che io non utilizzare il DefaultTreeModel. Implemento il mio TreeModel, quindi non posso usare la roba DefaultXXX. Il problema è questo: attraverso alcuni metodi addStuff() che il mio modello definisce, aggiungo i nodi alla struttura dati sottostante. Ho poi notificare gli ascoltatori chiamando treeNodesChanged() all'interno della funzione addStuff() (so che ci sono metodi treeNodesInserted ma è la stessa cosa. Si segnala solo gli ascoltatori con un metodo diverso). Ora, uno degli ascoltatori è una classe statica nella mia forma principale e questo ascoltatore può dire al JTree, che è anche contenuto nel mio modulo principale, per aggiornarsi. Come faccio a dire al JTree di "ricaricare" alcuni o tutti i suoi nodi dal modello?Come posso aggiornare un JTree dopo aver aggiunto alcuni nodi al modello sottostante?

UPDATE: Trovato questo question che sebbene non esattamente la stessa, dà la risposta che voglio.

UPDATE 2: Il mio problema non era come notificare il visualizzatore (il JTree), ma piuttosto in che modo il jtree dovrebbe essere ricaricato dopo la notifica dal modello.

Prima di tutto lasciatemi dire che l'unico modo che conosco per aggiornare un albero per riflettere le modifiche sottostanti, è chiamare l'updateUI(), o riutilizzare il metodo setModel(). In sostanza, il mio problema è questo:

Supponiamo che TreeModelListener sia stato appena notificato (tramite l'API TreeModelListener) che si è verificata una modifica nel modello. Ok, e adesso?

ho questo problema perché il JTree non implementa TreeModelListener. Quindi l'ascoltatore, nella mia situazione, è il contenitore di JTree, o una classe interna che implementa l'ascoltatore, che vive sotto lo stesso contenitore di Jtree.

Quindi supponiamo che io sono un'implementazione TreeModelListener, che vivono felicemente in un JForm con mio fratello JTree. All'improvviso viene chiamato il metodo treeNodesInserted (TreeModelEvent evt). Cosa faccio ora? Se chiamo Jtree.updateUI() dall'interno di me, l'elenco degli ascoltatori del modello genera un'eccezione di concurrentmodification. Posso chiamare qualcos'altro oltre a updateUI?

ho provato un certo numero di cose, ma solo updateUI rinfrescato la JTree. Quindi l'ho fatto al di fuori dell'ascoltatore. Da JForm, chiamo solo il metodo del modello che altera la struttura undrlying, e poi chiamo updateUI. Non viene usato TreeModelListener.

UPDATE3: Ho scoperto che sono stati registrati TreeModelListeners impliciti. In (ascoltatore TreeModelListener) implementazione addTreeModelListener del mio modello ho messo una linea di debug System.out:

System.out.println("listener added: " + listener.getClass().getCanonicalName()); 

e ho visto questo output di debug proprio quando ho eseguito jTree.setModel (modello):

ascoltatore aggiunto : javax.swing.JTree.TreeModelHandler

ascoltatore ha aggiunto: javax.swing.plaf.basic.BasicTreeUI.Handler

Il Concur rentModificationException è causato dal fatto che una chiamata a jtree.updateUI() registra nuovamente il listener (solo il file plaf, non entrambi) quindi viene generato quando richiamo updateUI all'interno di un ciclo di notifica del listener. L'unico modo ora per aggiornare l'albero è farlo al di fuori di TreeModelListener. Eventuali commenti o idee per una soluzione migliore? Mi sto perdendo qualcosa?

+1

Se hai trovato la tua risposta, è possibile chiudere la questione –

+1

E sarebbe bello pubblicare la soluzione si è utilizzato. – camickr

+1

Poiché la mia modifica ha origine da un metodo in un JForm (che contiene il modello e il Jtree), non ho usato affatto TreeModelListener. Ho appena chiamato addStuff del modello() che inserisce i nodi nella struttura del modello e poi richiamo manualmente updateUI() sulla struttura. Se ho implementato updateUI() in un'implementazione TreeModelListener in JForm, ho ottenuto un'eccezione di modifica simultanea nell'elenco dei listener. Non ho investigato molto e sono sicuro che non è una soluzione elegante, ma al momento sto facendo prototipi e non mi interessa per le implementazioni interne al momento. – Paralife

risposta

15

Ho affrontato lo stesso "problema": chiamando treeNodesInserted() non ha causato il mio JTree per aggiornare il suo contenuto.

Ma il problema era altrove: ho utilizzato il costruttore errato per TreeModelEvent. Ho pensato che posso creare TreeModelEvent per treeNodesInserted() così:

//-- Wrong!! 
TreePath path_to_inserted_item = /*....*/ ; 
TreeModelEvent tme = new TreeModelEvent(my_source, path_to_inserted_item); 

questo non funziona.

Come indicato in TreeModelEvent docs, questo costruttore è necessario solo per treeStructureChanged().Ma per treeNodesInserted(), treeNodesRemoved(), treeNodesChanged() dovremmo usare un altro costruttore:

TreePath path_to_parent_of_inserted_items = /*....*/ ; 
int[] indices_of_inserted_items = /*....*/ ; 
Object[] inserted_items = /*....*/ ; 
TreeModelEvent tme = new TreeModelEvent(
     my_source, 
     path_to_parent_of_inserted_items, 
     indices_of_inserted_items, 
     inserted_items 
    ); 

Questo codice funziona, e JTree aggiorna il suo contenuto in modo corretto.


UPD: In realtà, i documenti non sono chiare sull'utilizzo di questi TreeModelEvent s, e soprattutto con JTree, quindi, voglio raccontare alcune domande che mi è venuta quando ho cercato di capire come affrontare tutta questa roba

In primo luogo, come notato da Paralife, il suo commento, i casi in cui i nodi sono inseriti/modificati/rimossi o quando la struttura ad albero è cambiata, non sono ortogonali. Così,

Domanda # 1: quando dovremmo usare treeNodesInserted()/Changed()/Removed(), e quando treeStructureChanged()?

Risposta:treeNodesInserted()/Changed()/Removed() può essere utilizzato solo se tutti i nodi interessati hanno lo stesso genitore. In caso contrario, è possibile effettuare più chiamate a questi metodi o chiamare semplicemente treeStructureChanged() una volta (e passare il nodo root dei nodi interessati ad esso). Quindi, treeStructureChanged() è una sorta di modo universale, mentre treeNodesInserted()/Changed()/Removed() sono più specifici.

Domanda # 2: Per quanto riguarda treeStructureChanged() è un modo universale, perché ho bisogno di trattare con questi treeNodesInserted()/Changed()/Removed()? Basta chiamare per treeStructureChanged() sembra essere più facile.

Risposta: Se si utilizza JTree per visualizzare il contenuto del tuo albero, quindi la seguente cosa potrebbe essere un atri: per voi (come è stato per me): quando si chiama treeStructureChanged(), quindi JTree non significa mantenere espandere Stato dei sottonodi! Si consideri l'esempio, ecco i contenuti della nostra JTree ora:

[A] 
|-[B] 
|-[C] 
| |-[E] 
| | |-[G] 
| | |-[H] 
| |-[F] 
|  |-[I] 
|  |-[J] 
|  |-[K] 
|-[D] 

Poi si apportare alcune modifiche al C (ad esempio, rinominarlo in C2), e si chiama treeStructureChanged() per questo:

myTreeModel.treeStructureChanged(
     new TreeModelEvent(
      this, 
      new Object[] { myNodeA, myNodeC } // Path to changed node 
      ) 
     ); 

Poi, i nodi E e F verranno compressi! E la tua JTree sarà simile a quanto segue:

[A] 
|-[B] 
|-[C2] 
| +-[E] 
| +-[F] 
|-[D] 

Per evitare questo, si dovrebbe usare treeNodesChanged(), come quella:

myTreeModel.treeNodesChanged(
     new TreeModelEvent(
      this, 
      new Object[] { myNodeA }, // Path to the _parent_ of changed item 
      new int[] { 1 },   // Indexes of changed nodes 
      new Object[] { myNodeC }, // Objects represents changed nodes 
            // (Note: old ones!!! 
            //  I.e. not "C2", but "C", 
            //  in this example) 
      ) 
     ); 

Poi, lo stato in espansione verrà mantenuta.


Spero che questo post sia utile per qualcuno.

+0

Ok, 4 anni dopo la domanda non posso verificarlo. In effetti, non ho toccato materiale java da allora. Comunque, ho letto i documenti che hai citato e, in effetti, i documenti differenziano il costruttore basandosi su se la struttura è cambiata o se i nodi sono stati inseriti/rimossi. Ma questi due casi non sono ortogonali. La struttura ad albero non cambia quando un nodo viene inserito/rimosso? La descrizione del costruttore che uso dice: "Usato per creare un evento quando la struttura del nodo è cambiata in qualche modo". Comunque lo contrassegnerò come corretto. Grazie. – Paralife

+0

@Paralife, sì, questi casi non sono ortogonali. In realtà, i documenti non sono chiari su alcune cose, quindi ho aggiornato la mia risposta (aggiunta di spiegazioni su cose che per me non erano chiare personalmente), controlla se vuoi. Grazie per aver contrassegnato la mia risposta, tra l'altro =) –

+0

Ciao Dammy !! Ho trovato la tua risposta molto utile ... Grande grazie ... volevo sapere che codice/riga dovrei scrivere all'interno di treeNodesChanged() per aggiornare effettivamente l'interfaccia utente .. Come faccio a usare l'oggetto TreeModelEvent che ho avuto per aggiornare quella particolare interfaccia utente parte dell'albero? – ayush

3

Il TreeModel deve attivare TreeModelEvents quando cambia e JTree osserva il modello con un TreeModelListener per aggiornarsi quando il modello cambia.

Quindi, se si attua il sostegno TreeModelListener correttamente, non è necessario osservare il modello e informare il Jtree, come fa già così in sé. Da una prospettiva MVC, JTree è il tuo View/Controller e TreeModel è il tuo Model (o meglio: model adapter), che è osservabile.

Si potrebbe provare a forzare il JTree ad aggiornare il suo stato visivo chiamando repaint(), ma io raccomanderei di non farlo perché non è garantito il funzionamento. Quando non si è sicuri di come eseguire una notifica granulare a un TreeModelListener, utilizzare TreeModelListener.treeStructureChanged (..) per notificare l'aggiornamento di un 'modello intero' (avviso: può causare la perdita di selezioni e stati di espansione del nodo).

+0

Il mio problema non era come notificare al visualizzatore (il JTree), ma piuttosto in che modo il jtree dovrebbe ricaricarsi dopo la notifica dal modello. – Paralife

+0

E anche se è possibile per il jtree ricaricare solo una porzione. – Paralife

+0

Non è necessario implementare 'TreeModelListener', poiché' JTree' ha il proprio listener. Si dovrebbe chiamare 'treeNodesInserted()' con ** oggetto 'TreeModelEvent' corretto **. Vedi la mia risposta per i dettagli. –

12

Ho sempre trovato il TreeModel un po 'confuso. Sono d'accordo con la dichiarazione di cui sopra che il modello dovrebbe notificare la vista quando viene apportata una modifica in modo che la vista possa ridisegnare se stessa. Tuttavia, questo non sembra essere il caso quando si utilizza DefaultTreeModel. Trovo che è necessario richiamare il metodo reload() dopo aver aggiornato il modello. Qualcosa di simile:

DefaultTreeModel model = (DefaultTreeModel)tree.getModel(); 
DefaultMutableTreeNode root = (DefaultMutableTreeNode)model.getRoot(); 
root.add(new DefaultMutableTreeNode("another_child")); 
model.reload(root); 
+0

DefaultTreeXXXX API non è un'opzione. Implemento il mio modello di albero. – Paralife

+0

in realtà, io sono carina, non useresti il ​​martello structuryChanged per un semplice inserto ;-) Che è supportato in DefaultTreeModel api - usa semplicemente quello invece di modificare il sottostante dataStrucure (aka: nodo di cablaggio) sotto i piedi del modello – kleopatra

2

aggiornamento finale: trovato il problema e risolto : La seguente procedura a risolvere il problema, (ma si veda la risposta accettata per una soluzione migliore e una profonda spiegazione del problema):

  1. Gli ascoltatori impliciti registrati sono sufficienti per svolgere il lavoro. Non c'è bisogno di implementare il mio ascoltatore.
  2. Quando si aggiungono nodi e si chiama treeNodesInserted() non funziona (JTree non aggiornato). Funziona con la chiamata treeStructureChanged().

Apparentemente gli ascoltatori impliciti stanno aggiornando internamente l'albero nel modo che preferisco, ma solo nella loro implementazione del metodo treeStructureChanged(). Sarebbe bene per JTree fornire questo "algoritmo" come funzione per poter essere chiamato manualmente.

+1

Tutti di Swing si basa sull'idea che un modello dice alla sua vista di rinfrescarsi, e la vista lo capisce. Stai cercando un'API che rompa questo. – kdgregory

+1

Ho pensato che il principio non fosse "il modello dice alla vista di aggiornare", ma piuttosto "il modello dice alla vista che è cambiato" e la vista dovrebbe essere libera di agire come vuole. Forse la vista non vuole aggiornarsi. Forse vuole continuare a ricevere notifiche, ma penso che sia un po 'restrittivo vedere il modello dire implicitamente alla vista cosa fare dopo essere stato avvisato.Nella situazione particolare, non romperebbe nulla e sarebbe meglio se il jtree fornisse solo un metodo refreshPath(), e potrei semplicemente implementare il mio listener e farlo chiamare il metodo quando mai lo volessi. – Paralife

+0

Il modo in cui dovrebbe funzionare secondo l'approccio MVC è che il modello è monitorato per le modifiche da un Observer o Listener e le modifiche vengono passate alla vista. Di solito questo viene fatto da un controllore di classe se agisce da mediatore. –

4

Ho anche trovato che l'implementazione di TreeModel ha un po 'di confusione quando l'albero consiste in più di cartelle (root) e file (figlio), quindi ho usato DefaultTreeModel. Questo funziona per me. Il metodo crea o aggiorna JTree. Spero che questo ti aiuti.

public void constructTree() {  

     DefaultMutableTreeNode root = 
       new DefaultMutableTreeNode(UbaEnv.DB_CONFIG); 
     DefaultMutableTreeNode child = null; 

     HashMap<String, DbConfig> dbConfigs = env.getDbConfigs(); 
     Iterator it = dbConfigs.entrySet().iterator(); 
     while (it.hasNext()) { 
      Map.Entry pair = (Map.Entry) it.next(); 
      child = new DefaultMutableTreeNode(pair.getKey()); 

      child.add(new DefaultMutableTreeNode(UbaEnv.PROP)); 
      child.add(new DefaultMutableTreeNode(UbaEnv.SQL)); 
      root.add(child); 
     } 

     if (tree == null) { 
      tree = new JTree(new DefaultTreeModel(root)); 
      tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); 
      tree.addTreeSelectionListener(new TreeListener()); 
     } else { 
      tree.setModel(new DefaultTreeModel(root)); 
     } 
} 
2

Ieri ho lottato per risolvere lo stesso problema.Il requisito era inserire e rimuovere i nodi al volo, senza comprimere i nodi dell'albero espansi. Ho navigato in rete e ho trovato un sacco di soluzioni possibili, finché non sono inciampato in questa discussione. Quindi ho applicato l'anwser da "Dmitry Frank" con lo TreeModelEvent. Ero un po 'confuso, perché è un problema così grande semplicemente inserire o rimuovere un nodo semplice e lasciare il resto del JTree non toccato! Infine, i semplici esempi di vaniglia da java2s mi hanno aiutato a trovare la soluzione probabilmente più semplice. (Né una chiamata del genere: è stato richiesto nodeStructureChanged, nodeChanged, nodesWereRemoved, nodesWereInserted, ecc né una TreeModelEvent come suggerito da 'Dmitrij Frank'.)


Ecco la mia soluzione:

// Remove a node 
treeModel.removeNodeFromParent(myNode); 

// Insert a node (Actually this is a reinsert, since I used a Map called `droppedNodes` to keep the previously removed nodes. So I don't need to reload the data items of those nodes.) 
MyNode root = treeModel.getRootNode(); 
treeModel.insertNodeInto(droppedNodes.get(nodeName), root, root.getChildCount()); 

Resta semplice;)

0

Ad esempio: Jtree Aggiorna dinamicamente

package package_name; 
import javax.swing.tree.DefaultMutableTreeNode; 
import javax.swing.tree.DefaultTreeModel; 

public final class homepage extends javax.swing.JFrame implements ActionListener 
{ 
    DefaultTreeModel model; 
    DefaultMutableTreeNode root; 
    File currentDir = new File("database"); 

public homepage() throws InterruptedException { 
     initComponents(); 
    //------full screen window 
     this.setExtendedState(MAXIMIZED_BOTH); 
    //====================Jtree Added Details.. 

    //result is the variable name for jtree 
     model =(DefaultTreeModel) treedbnm.getModel(); 
    //gets the root of the current model used only once at the starting 
     root=(DefaultMutableTreeNode) model.getRoot(); 
    //function called 
     dbcreate_panel1(); 
     Display_DirectoryInfo(currentDir,root); 

}//End homepage() Constructor 
public void Display_DirectoryInfo(File dir,DefaultMutableTreeNode tmp_root) throws InterruptedException 
{.......... 
} 
public void dbcreate_panel1() 
{ 
    //------------------Jtree Refresh Dynamically-------------------// 
      root.removeFromParent(); 
      root.removeAllChildren(); 
      model.reload(); 
      Display_DirectoryInfo(currentDir,root); 
} 
}//End homepage 
0

Sembra possibile reinizializzare l'intero albero impostando il modello su null: ad es.

 TreePath path = tree.getSelectionPath(); 
     model.doChanges(); // do something with model 
     tree.setModel(null); 
     tree.setModel(model); 
     tree.setSelectionPath(path); 
     tree.expandPath(path); 

aggiornamento Albero funziona per me

kr Achim

Problemi correlati