# Jtree Knoten ändern



## Zipp (2. Mai 2010)

Ich habe mich am JTree mal versucht.
Dank diesem Tutorual win ich auch schon weit gekommen. Mein Problem ist jetzt das ändern von Knoten.

Ich habe die Klassen aus dem vierten Teil überneommen und versucht sie so zu erweitern, dass ich Knoten umbennen kann. Dafür habe ich die Klasse ChangeableModel um folgende Klasse erweitert:

```
public void change(TreePath path, MutableTreeNode node) {
    Object parent = path.getParentPath().getLastPathComponent();
    Object child = path.getLastPathComponent();
    int index = getIndexOfChild(parent, child);
    ((MutableTreeNode)parent).insert(node, index);

    TreeModelEvent event = new TreeModelEvent(this, path.getParentPath(),
        new int[] { index }, new Object[] { node });
    for (TreeModelListener listener : listeners) {
      listener.treeNodesChanged(event);
    }
  }
```

Desweiteren habe ich eine Klasse ChangeAction angelegt, die sich an den Klassen AddAction und RemoveAction orierntiert:

```
class ChangeAction extends TreeAction {
  public ChangeAction(JTree tree, ChangeableModel model) {
    super(tree, model);
  }
  
  @Override
  protected void invoked() {
    TreePath[] paths = tree.getSelectionPaths();
    if (paths != null) {
      tree.clearSelection();
      for (TreePath path : paths) {
        MutableTreeNode node = new MutableTreeNode("changed");
        model.change(path, node);
      }
    }
  } 
}
```

Jetzt zu meinem Problem:
Der Knoten wird wie erwartet ausgetauscht und es wird auch "changed" angezeigt. Problem ist, das das Icon nicht mehr stimmt, sowie die Linien vor dem Icon (bei Windows ist es ein + und ein - Zeichen). Wenn ich einen Knoten ändern wird immer das Icon für ein Blatt angezeigt.
Weiß jemand welchen fehler ich gemacht habe?
Und eine kleinere Frage: Wie bekommt man denn TreePath, zu dem Knoten den ich gerade geändert habne, so dass ich ihn über 
	
	
	
	





```
tree.addSelectionPath
```
 auswählen kann?


----------



## Ebenius (2. Mai 2010)

Du machst den Fehler, dass Du den Knoten zum Parent hinzufügst, den alten Knoten aber nicht entfernst. Und Du sagst den Zuhörern (also dem JTree) [c]listener.treeNodesChanged(event);[/c]. Das passt erstens nicht zum hinzufügen des Knotens. Und zweitens ist dieser Event-Typ dafür gedacht, dass sich der Wert eines Knotens ändert, die Struktur aber nicht. Da jedoch der alte Knoten ggf. Kinder hatte, oder der neue welche haben kann, musst Du eine Strukturänderung bekannt geben. Probier's mal so (ungetestet):
[java=5]  public void change(TreePath path, MutableTreeNode node) {
    Object parent = path.getParentPath().getLastPathComponent();
    Object child = path.getLastPathComponent();
    int index = getIndexOfChild(parent, child);
    ((MutableTreeNode)parent).remove(index);
    ((MutableTreeNode)parent).insert(node, index);

    TreeModelEvent event = new TreeModelEvent(this, path.getParentPath(),
        new int[] { index }, new Object[] { node });
    for (TreeModelListener listener : listeners) {
      listener.treeStructureChanged(event);
    }
  }[/code]

Den neuen Pfad machst Du einfach aus dem alten...

```
TreePath newPath = path.getParentPath().pathByAddingChild(newNode)
```
Ebenius


----------



## Zipp (2. Mai 2010)

Danke für die schnelle Antwort. So ähnlich hatte ich das auch mal ausprobiert, aber beim Entfernen des "alten" Knotens werden auch seine Kinder gelöscht, sodass diese dann nicht mehr am neuen hängen.


----------



## Ebenius (2. Mai 2010)

Hm, aber wenn Du den Knoten nicht ändern möchtest (der Knoten enthält ja die Kinder) sondern nur das User Object (meist ein Text), dann solltest Du das anders machen. In etwa: 


```
public void setNodeValue(TreePath path, Object value) {
    Object parent = path.getParentPath().getLastPathComponent();
    Object child = path.getLastPathComponent();
    int index = getIndexOfChild(parent, child);
    ((MutableTreeNode)child).setUserObject(value);
 
    TreeModelEvent event = new TreeModelEvent(this, path.getParentPath(),
        new int[] { index }, new Object[] { node });
    for (TreeModelListener listener : listeners) {
      listener.treeNodesChanged(event);
    }
  }
```
Ebenius


----------



## Zipp (18. Aug 2010)

Beim Thema JTree bin ich wieder an ein Problem gekommen, welches ich irgendwie nicht nachvollziehen kann. Ich möchte, dass die Kinder eines Knoten in einer bestimten Reihenfolge (beispielsweise alphabetisch). Daher übergebe ich der addNode-Methode neben dem Pfad zum Eltern-Knoten und dem neuen Knoten auch noch einen Index um den Knoten an einer besimmten stelle einzufügen. 

Das Problem liegt natürlich wieder beim verändern des Knotens. Wenn das UserObject geändert wird, kann es sein, dass es in einer anderen Position muss, damit die Reihenfolge weiter stimmt.

Daher habe ich mir Folgendes überlegt:

```
public void changeUserObject(TreePath path, Object newUserObject, int newIndex) {
    DefaultMutableTreeNode parent = (DefaultMutableTreeNode) path.getParentPath().getLastPathComponent();
    DefaultMutableTreeNode child = (DefaultMutableTreeNode) path.getLastPathComponent();
    int oldIndex = getIndexOfChild(parent, child);
    
    child.setUserObject(newUserObject);
    

    if (oldIndex != newIndex) {
      if (newIndex > oldIndex) {
        --newIndex;
      }
      parent.remove(oldIndex); 
      parent.insert(child, newIndex);
    }

    int[] indices = new int[parent.getChildCount()];
    for (int i=0; i < indices.length; i++) {
      indices[i] = i;
    }
    
    Object[] children = new Object[parent.getChildCount()];
    for (int i=0; i < children.length; i++) {
      children[i] = parent.getChildAt(i);
    }
    
    TreeModelEvent event = new TreeModelEvent(this, path.getParentPath(), indices, children);
    for (TreeModelListener listener : listeners) {
      listener.treeNodesChanged(event);
      //listener.treeStructureChanged(event);
    }
  }
```
Ich ändere das UserObject und wenn die beiden Positionen nicht übereinstimmten, wird der Knoten aus der Rheinfolge gelöscht und wieder hinzugefügt. Danach erstelle ich zwei Arrays für die Positionen und Objekte die geändert wurden. Um es einfach zu machen füge ich jedes Kind ein.

Das Model ist richtig, aber die Anzeige nicht. Manchmal muss ich auf einen Knoten klicken, damit er aktualisiert wird und das richtige UserObject angezeigt wird. Das kann ich mir eigentlich nicht erklären, da doch jedens Objekt in den Arrays aktualisiert werden soll, oder nicht?:bahnhof:
Wenn ich neben treeNodesChanged() auch noch treeStructureChanged() (auskommentierte Zeile) aufrufe, dann simmt es, aber die Tree kollabiert dann teilweise, was ich auch nicht haben will.

Hat jemand eine Idee? Oder habe ich wieder einen grundsätzlichen Denkfehler?


----------



## Ebenius (19. Aug 2010)

Zipp hat gesagt.:


> Das Model ist richtig, aber die Anzeige nicht.


Diese Aussage ist wahrscheinlich falsch. Das Modell setzt die falschen Events ab, deswegen aktualisiert sich der JTree nicht dort wo man das möchte. Ergo ist eigentlich das Modell falsch implementiert.



Zipp hat gesagt.:


> Wenn ich neben treeNodesChanged() auch noch treeStructureChanged() (auskommentierte Zeile) aufrufe, dann simmt es, aber die Tree kollabiert dann teilweise, was ich auch nicht haben will.


treeStructureChanged() ist sehr schwergewichtig. Danach liest der JTree das gesamte Modell neu ein. Dann stimmt natürlich alles wieder. Aber das will man so wirklich nicht. Was Du machen musst sieht so aus (PseudoCode): 
	
	
	
	





```
public void valueForPathChanged(TreePath path, Object newValue) {
  if (sortierungÄndertSichNicht) {
    // Hier neuen Wert im Modell abspeichern
    fireTreeNodesChanged(this, path.getParentPath().getPath(), new int[] { indexDesKnotenImElternknoten }, new Object[] { newValue });
  } else {
    // Hier Knoten entfernen
    fireTreeNodesRemoved(this, path.getParentPath().getPath(), new int[] { indexDesEhemaligenKnotenImElternknoten }, new Object[] { oldValue });
    // Hier neuen Knoten an geänderter Position wieder eintragen
    fireTreeNodesInserted(this, path.getParentPath().getPath(), new int[] { indexDesNeuenKnotenImElternknoten }, new Object[] { newValue });
  }
}
```
Ebenius


----------

