2009-12-10 6 views
5

J'ai un problème et j'espère, quelqu'un sait ce qui ne va pas et pourquoi et est capable de me donner l'explication de ce qui me manque en ce moment pour faire fonctionner cette chose comme suggéré.Swing question/JTree/modèle d'arbre personnalisé

J'ai un JTree qui est construit sur un TreeModel personnalisé ("WRTreeModel", voir ci-dessous). La structure de données pour laquelle ce modèle doit être utilisé est la construction d'un objet racine qui contient des champs et en outre une liste qui est sauvegardée par le "ArrayListModel" montré ci-dessous. L'arborescence est parfaite lorsque je la compile à l'aide de WRTreeModel. Je suis capable de développer et de réduire les nœuds qui représentent les listes et les champs contenus dans les objets. Je peux développer et réduire ces listes et voir leur contenu et ainsi de suite.

Maintenant, je veux supprimer un enfant de l'une des listes et - comme je le sais déjà - le faire en le supprimant du modèle appelant la méthode remove de ArrayListModel. Pour rendre le WRTreeModel conscient de cette suppression, la première chose à faire est d'appeler sa méthode fireIntervalRemoved qui est appelée, jusqu'ici tout va bien.

Dans la WRTreeModels classe interne ArrayModelListener la méthode intervalRemoved prépare l'appel de fireTreeNodesRemoved qui a ensuite construit une TreeEvent qui est transmis à tous les inscrits TreeModelListeners (et donc le JTree qui s'enregistre automaticall lorsqu'il est connecté au modèle).

Maintenant je m'attendrais à ce que l'arbre reflète le changement et mette à jour sa représentation interne et visuelle pour montrer le nouvel état. Malheureusement, cela ne semble pas fonctionner de cette façon. Quelque chose arrive. Mais quand je clique sur le nœud que j'ai juste changé, certaines EventHandler-Exceptions sont levées. De toute évidence, quelque chose est devenu vraiment confus.

Je sais que ce n'est pas facile de répondre à une telle question à la volée, mais j'apprécierais vraiment une réponse rapide. Il serait également utile, si quelqu'un connaissait des sites Web expliquant l'utilisation de modèles d'arborescence personnalisés (pas sur DefaultMutableTreeNode ou une classe basée sur l'implémentation donnée) et comment la gestion et la mise à jour des événements JTree fonctionnent.

Avec mes meilleures salutations,

Arts Thomas


public class ArrayListModel<E> extends ArrayList<E> implements ListModel { 

... 

public E remove(int index) { 
    fireIntervalRemoved(index, index); 
    E removedElement = super.remove(index); 
    return removedElement; 
    } 

... 

} 

public class WRTreeModel extends LogAndMark implements TreeModel { 


    class ArrayModelListener implements ListDataListener { 

    ... 

    @Override 
    public void intervalRemoved(ListDataEvent e) { 
     int[] indices = new int[e.getIndex1() - e.getIndex0() + 1]; 
     for (int i = e.getIndex0(); i < e.getIndex1(); i++) 
     indices[i - e.getIndex0()] = i; 
     fireTreeNodesRemoved(e.getSource(), getPathToRoot(e.getSource()), indices,  ((ArrayListModel<?>)e.getSource()).subList(e.getIndex0(), e.getIndex1()+1).toArray()); 
    } 

    ... 

    } 

    public Object[] getPathToRoot(Object child) { 
    ArrayList<Object> ret = new ArrayList<Object>(); 
    if (child == null) 
     return ret.toArray(); 
    ret.add(root); 
    if (child == root) 
     return ret.toArray(); 
    int childType = 0; 
    if (child instanceof List<?> && ((List) child).get(0) instanceof Einleitungsstelle) { 
     childType = 1; 
    } 
    if (child instanceof Einleitungsstelle) { 
     childType = 2; 
    } 
    if (child instanceof List<?> && ((List) child).get(0) instanceof Messstelle) { 
     childType = 3; 
    } 
    if (child instanceof Messstelle) { 
     childType = 4; 
    } 
    if (child instanceof List<?> && ((List) child).get(0) instanceof  Ueberwachungswert) { 
     childType = 5; 
    } 
    if (child instanceof Ueberwachungswert) { 
     childType = 6; 
    } 
    if (child instanceof List<?> && ((List) child).get(0) instanceof  Selbstueberwachungswert) { 
     childType = 7; 
    } 
    if (child instanceof Selbstueberwachungswert) { 
     childType = 8; 
    } 
    switch (childType) { 
    // List of ESTs 
    case 1: { 
     ret.add(child); 
     break; 
    } 
    // EST 
    case 2: { 
     List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST(); 
     ret.add(listOfEST); 
     ret.add(child); 
     break; 
    } 
    // List of MSTs 
    case 3: { 
     List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST(); 
     ret.add(listOfEST); 
     // Find the EST containing the List of MSTs the child referes to 
     for (Einleitungsstelle einleitungsstelle : listOfEST) { 
     if (child == einleitungsstelle.getListOfMST()) { 
      ret.add(einleitungsstelle); 
      break; 
     } 
     } 
     ret.add(child); 
     break; 
    } 
    // MST 
    case 4: { 
     List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST(); 
     ret.add(listOfEST); 
     // Find the EST containing the List of MSTs the child referes to 
     for (Einleitungsstelle einleitungsstelle : listOfEST) { 
      if (child == einleitungsstelle.getListOfMST()) { 
      ret.add(einleitungsstelle.getListOfMST()); 
      break; 
      } 
     } 
     ret.add(child); 
     break; 
    } 
    // List of UEWs 
    case 5: { 
     List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST(); 
     ret.add(listOfEST); 
     // Find the EST containing the List of MSTs the child referes to 
     for (Einleitungsstelle einleitungsstelle : listOfEST) { 
     ArrayListModel<Messstelle> listOfMST = einleitungsstelle.getListOfMST(); 
     if (child == listOfMST) { 
      ret.add(listOfMST); 
      for (Messstelle messstelle : listOfMST) { 
      if (child == messstelle.getListOfUEW()) { 
       ret.add(messstelle.getListOfUEW()); 
       break; 
      } 
      } 
      break; 
     } 
     } 
    break; 
    } 
    // UEW 
    case 6: { 
     List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST(); 
     ret.add(listOfEST); 
     // Find the EST containing the List of MSTs the child referes to 
     for (Einleitungsstelle einleitungsstelle : listOfEST) { 
     ArrayListModel<Messstelle> listOfMST = einleitungsstelle.getListOfMST(); 
     if (child == listOfMST) { 
      ret.add(listOfMST); 
      for (Messstelle messstelle : listOfMST) { 
      if (child == messstelle.getListOfUEW()) { 
       ret.add(messstelle.getListOfUEW()); 
       break; 
      } 
      } 
      break; 
     } 
     } 
     ret.add(child); 
     break; 
    } 
    // List of SUEWs 
    case 7: { 
     List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST(); 
     ret.add(listOfEST); 
     // Find the EST containing the List of MSTs the child referes to 
     for (Einleitungsstelle einleitungsstelle : listOfEST) { 
     ArrayListModel<Messstelle> listOfMST = einleitungsstelle.getListOfMST(); 
     if (child == listOfMST) { 
      ret.add(listOfMST); 
      for (Messstelle messstelle : listOfMST) { 
      if (child == messstelle.getListOfSUEW()) { 
       ret.add(messstelle.getListOfSUEW()); 
       break; 
      } 
      } 
      break; 
     } 
     } 
     break; 
    } 
    // SUEW 
    case 8: { 
     List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST(); 
     ret.add(listOfEST); 
     // Find the EST containing the List of MSTs the child referes to 
     for (Einleitungsstelle einleitungsstelle : listOfEST) { 
      ArrayListModel<Messstelle> listOfMST = einleitungsstelle.getListOfMST(); 
      if (child == listOfMST) { 
      ret.add(listOfMST); 
      for (Messstelle messstelle : listOfMST) { 
      if (child == messstelle.getListOfSUEW()) { 
       ret.add(messstelle.getListOfSUEW()); 
       break; 
      } 
      } 
      break; 
     } 
     } 
     ret.add(child); 
     break; 
     } 
     default: 
     ret = null; 
    } 
    return ret.toArray(); 
    } 
    } 

... 

    protected void fireTreeNodesRemoved(Object changed, Object path[], int  childIndecies[], Object children[]) { 
     TreeModelEvent event = new TreeModelEvent(this, path, childIndecies, children); 
     synchronized (listeners) { 
     for (Enumeration e = listeners.elements(); e.hasMoreElements();) { 
     TreeModelListener tml = (TreeModelListener) e.nextElement(); 
     tml.treeNodesRemoved(event); 
     } 
     } 
    } 

... 

} 
+0

Ne sait pas si c'est d'aide supplémentaire, mais quand j'essaye de réduire puis de développer le nœud parent du nœud supprimé, je reçois l'exception suivante: Exception dans le fil "AWT-EventQueue-0" java.lang. NullPointerException \t à javax.swing.plaf.basic.BasicTreeUI.ensureRowsAreVisible (BasicTreeUI.java:1881) \t à javax.swing.plaf.basic.BasicTreeUI.toggleExpandState (BasicTreeUI.java:2208) \t à javax.swing. plaf.basic.BasicTreeUI.handleExpandControlClick (BasicTreeUI.java:2191) \t at javax.swing.plaf.base.BasicTreeUI.checkForClickInExpandControl (BasicTreeUI.java:2149) \t à ... et ainsi de suite –

+0

Tous les problèmes sont urgents par définition :-) Je ne mentionnerais pas "pression du temps" dans une question. Et votre question est destinée à rester et à aider les autres quand vous l'oubliez depuis longtemps. Le rend plus canonique. – raoulsson

Répondre

2

Vous devez effectuer le nœud et supprimer l'événement TreeModelListener.treeNodesRemoved après le tir sur le fil de répartition de l'événement.

Pour ce faire, utilisez:

SwingUtilities.invokeLater(
    new Runnable() 
    { 
    public void run() 
    { 
     //Delete and event firing logic goes here. 
     ... 
    } 
    } 
); 

Faire cela empêche swing en utilisant l'EDT pour mettre à jour l'arbre au milieu de votre suppression et la mise à feu de l'événement indique le contrôle de DTree (qui a ajouté des auditeurs) que le modèle a changé.

+0

Cela et en outre un Tree.updateUI() avec un expandToPath (chemin) suivant avec le chemin de le parent des éléments supprimés fait exactement le travail que je voulais. Merci beaucoup pour cet indice! BTW: Il semble que l'utilisation de SwingUtilities.invokeLater soit si courante que personne ne pense jamais que les autres oublient d'y mettre leurs événements et leurs éléments d'interface utilisateur. –

+0

+1 pour étoffer mon idée. –

+0

@Carl: merci :-) –

0

Parce que nous sommes pressés, je ne l'ai pas regardé encore votre code. Votre description a l'air de tout faire correctement et de penser à ce qui est nécessaire.

Ma conjecture sur quelque chose que vous n'avez peut-être pas pris en compte: ce changement de modèle d'arbre se produit-il dans le fil d'envoi d'événement (alias Swing worker thread)? Si le changement provient d'un autre thread, il est probable qu'il ne sera pas traité correctement.

Juste un coup de couteau dans le noir, bien sûr.

+0

Je pense que cela se passe dans le bon fil. Lorsque je débogue avec un point d'arrêt dans la méthode fireTreeNodesRemoved, il s'arrête dans le thread AWT-EventQueue. ce qui semble être juste. –

+0

Vous avez raison. Voir ci-dessus réponse qui a suggéré à cela, aussi et en fait cela fonctionne comme un charme :-) –

+0

Excellent! Je suis désolé d'avoir été pressé aussi (dû renflouer pour une réunion) et n'ai pas eu la chance de recommander le correctif. –

0

On dirait que vous avez un bogue off-by-one dans intervalRemoved.

Vous n'initialisez pas la dernière valeur du tableau d'indices. Il sera auto-initialisé à 0.

@Override 
public void intervalRemoved(ListDataEvent e) { 
    int[] indices = new int[e.getIndex1() - e.getIndex0() + 1]; 
    for (int i = e.getIndex0(); i < e.getIndex1(); i++) 
    indices[i - e.getIndex0()] = i; 
    fireTreeNodesRemoved(e.getSource(), getPathToRoot(e.getSource()), indices,  ((ArrayListModel<?>)e.getSource()).subList(e.getIndex0(), e.getIndex1()+1).toArray()); 
} 

Essayez plutôt "i < = e.getIndex1()":

for (int i = e.getIndex0(); i <= e.getIndex1(); i++) { 
    indices[i - e.getIndex0()] = i; 
} 
+0

Vous avez absolument raison avec ça. La même chose est indiquée dans la description de la méthode. J'ai corrigé cela même si cela n'avait aucune influence sur mon problème puisque l'élément de liste que j'essaye de supprimer est le premier et donc l'index 0 est correct. –

0

Le nom de la méthode est fireIntervalRemoved, alors essayez de l'appeler après suppression:

public E remove(int index) { 
    E removedElement = super.remove(index); 
    fireIntervalRemoved(index, index); 
    return removedElement; 
} 

C'est comme ça que j'ai fait et j'ai toujours travaillé (peut-être ajouté quelques vérifications).
(désolé si je raté quelque chose, did'nt obtenir le temps de analise/tester votre code ...)

+0

N'ai-je pas besoin de connaître l'élément supprimé pour créer mon TreeEvent? Si j'appelle fireIntervalRemoved (index, index) après avoir supprimé l'élément, je ne peux plus l'obtenir. C'est pourquoi j'ai déplacé l'appel avant la suppression réelle de la liste. –

+0

pas sûr, n'ont pas vérifié tout le code et ne peuvent pas trouver 'fireIntervalRemoved'. Le TreeModelEvent n'a pas besoin de l'élément, comme je le sais ... Je pense que si vous déclenchez l'événement et que l'interface graphique est mise à jour (assez rapidement) avant de supprimer l'élément, l'arbre ne reflétera pas la suppression (puisque cela n'est pas encore fait). Peut-être que vous pouvez construire l'événement avant de supprimer l'élément et déclencher l'événement après avoir supprimé ... ((* Merci Stuttgart *)) –

Questions connexes