Wie hier vorbereitet bitte ich Euch um Hilfe bei der Lösung eines Problems mit meinem TreeModel.
Nach dem Hinzufügen eines neuen Knotens verschwindet der Inhalt des JTree. Ich finde leider den Fehler nicht und hoffe, dass Ihr mir bei der Lösung des Problems helfen könnt.
Wie immer anbei ein KSKB:
Die Main-Class:
package category;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.JTree;
import javax.swing.tree.TreePath;
/**
*
* @author l-ectron-x
*/
public class Main {
private CategoryTreeModel treeModel;
private JTree tree;
Main() {
JFrame f = new JFrame("JTree-Demo");
f.add(createActionBar(), BorderLayout.NORTH);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Category root = new Category();
treeModel = new CategoryTreeModel(root);
tree = new JTree(treeModel);
tree.setEditable(true);
tree.setRowHeight(20);
tree.setCellRenderer(new CategoryTreeCellRenderer());
tree.setSelectionRow(0);
tree.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
f.add(new JScrollPane(tree));
f.setSize(300, 600);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
Main main = new Main();
}
private JToolBar createActionBar() {
JToolBar tb = new JToolBar();
tb.add(new AbstractAction("Add") {
@Override
public void actionPerformed(ActionEvent e) {
Object o = tree.getLastSelectedPathComponent();
treeModel.addNode(o);
tree.expandPath(tree.getSelectionPath());
}
});
tb.add(new AbstractAction("Edit") {
@Override
public void actionPerformed(ActionEvent e) {
TreePath path = tree.getSelectionPath();
tree.startEditingAtPath(path);
}
});
tb.add(new AbstractAction("Remove") {
@Override
public void actionPerformed(ActionEvent e) {
Object node = tree.getLastSelectedPathComponent();
treeModel.removeNode(node);
}
});
return tb;
}
}
Das TreeModel:
package category;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
/**
*
* @author l-ectron-x
*/
class CategoryTreeModel implements TreeModel {
private Set<TreeModelListener> eventListenerSet = new HashSet<>();
private Category root;
CategoryTreeModel(Category root) {
this.root = root;
}
public void addNode(Object parent) {
int childIndex = this.getChildCount(parent);
Category child = null;
Category parentCategory = (Category) parent;
Type parentType = parentCategory.getType();
switch (parentType) {
case PROJECT:
child = new Category(root, "Server_" + (childIndex + 1), parentType);
root.add(child);
break;
case SERVER:
child = new Category(parentCategory, "Part_" + (childIndex + 1), parentType);
parentCategory.add(child);
break;
case PART:
child = new Category(parentCategory, "Page_" + (childIndex + 1), parentType);
parentCategory.add(child);
break;
default:
break;
}
System.out.println("fireTreeNodesInserted(" + parent + ", " + this.getTreePath(parent) + ", new int[]{" + childIndex + "}, new Object[]{" + child + "})");
fireTreeNodesInserted(parent, this.getTreePath(parent), new int[]{childIndex}, new Object[]{child});
}
public void removeNode(Object node) {
Category child = (Category) node;
Category parent = child.getParent();
int childIndex = this.getIndexOfChild(parent, child);
if (childIndex < 0) {
return;
}
parent.remove(child);
fireTreeNodesRemoved(parent, this.getTreePath(parent), new int[]{childIndex}, new Object[]{node});
}
@Override
public Object getRoot() {
return root;
}
@Override
public Object getChild(Object parent, int index) {
Category parentCategory = (Category) parent;
return parentCategory.get(index);
}
@Override
public int getChildCount(Object parent) {
Category parentCategory = (Category) parent;
return parentCategory.size();
}
@Override
public boolean isLeaf(Object node) {
return this.getChildCount(node) == 0;
}
@Override
public void valueForPathChanged(TreePath path, Object newValue) {
TreePath oldValue = path;
Category node = (Category) path.getLastPathComponent();
node.setName(newValue.toString());
fireTreeNodesChanged(oldValue, path, null, null);
}
@Override
public int getIndexOfChild(Object parent, Object child) {
Category parentCategory = (Category) parent;
if (parent == null || parentCategory.isEmpty() || child == null) {
return -1;
}
return parentCategory.indexOf(child);
}
@Override
public void addTreeModelListener(TreeModelListener l) {
eventListenerSet.add(l);
}
@Override
public void removeTreeModelListener(TreeModelListener l) {
eventListenerSet.remove(l);
}
/**
* Erzeugt aus dem übergebenen Element ein TreePath-Objekt.
*
* @param node das Element, von dem aus ein Pfad bis zum Wurzelknoten
* erzeugt werden soll
* @return das erzeugte TreePath-Objekt
*/
public TreePath getTreePath(Object node) {
List<Object> nodes = new ArrayList<>();
Category category = (Category) node;
do {
nodes.add(category);
} while ((category = category.getParent()) != null);
Collections.reverse(nodes);
return nodes.isEmpty() ? null : new TreePath(nodes.toArray());
}
/**
* Informiert alle registrierten Listener, die sich für diesen Ereignistyp
* interessieren.
*
* @param source der Elternknoten, dem neue Kindknoten hinzugefügt wurden
* @param path der Pfad zum Wurzelknoten
* @param childIndices die Indizes der neuen Kindknoten
* @param children die neuen Kindknoten-Objekte
*/
protected void fireTreeNodesInserted(Object source, TreePath path, int[] childIndices, Object[] children) {
TreeModelEvent event = new TreeModelEvent(source, path, childIndices, children);
Iterator<TreeModelListener> it = eventListenerSet.iterator();
while (it.hasNext()) {
it.next().treeNodesInserted(event);
}
}
/**
* Informiert alle registrierten Listener, die sich für diesen Ereignistyp
* interessieren.
*
* @param source der Elternknoten, der für die Erzeugung des Ereignisses
* verantwortlich ist
* @param path der Pfad zum Wurzelknoten
* @param childIndices die Indizes der geänderten Kindknoten
* @param children die geänderten Kindknoten-Objekte
*/
protected void fireTreeNodesChanged(Object source, TreePath path, int[] childIndices, Object[] children) {
TreeModelEvent event = new TreeModelEvent(source, path, childIndices, children);
Iterator<TreeModelListener> it = eventListenerSet.iterator();
while (it.hasNext()) {
it.next().treeNodesChanged(event);
}
}
/**
* Informiert alle registrierten Listener, die sich für diesen Ereignistyp
* interessieren. The event instance is lazily created using the parameters
* passed into the fire method.
*
* @param source der Elternknoten, aus dem Kindknoten entfernt wurden
* @param path der Pfad zum Wurzelknoten
* @param childIndices die Indizes der entfernten Kindknoten
* @param children die entfernen Kindknoten-Objekte
*/
protected void fireTreeNodesRemoved(Object source, TreePath path, int[] childIndices, Object[] children) {
TreeModelEvent event = new TreeModelEvent(source, path, childIndices, children);
Iterator<TreeModelListener> it = eventListenerSet.iterator();
while (it.hasNext()) {
it.next().treeNodesRemoved(event);
}
}
/**
* Informiert alle registrierten Listener, die sich für diesen Ereignistyp
* interessieren. The event instance is lazily created using the parameters
* passed into the fire method.
*
* @param source der Knoten, an dem sich das Baummodell geändert hat
* @param path der Pfad zum Wurzelknoten
*/
protected void fireTreeStructureChanged(Object source, TreePath path) {
TreeModelEvent event = new TreeModelEvent(source, path);
Iterator<TreeModelListener> it = eventListenerSet.iterator();
while (it.hasNext()) {
it.next().treeStructureChanged(event);
}
}
}
Der TreeCellRenderer:
package category;
import java.awt.Component;
import javax.swing.JTree;
import javax.swing.tree.DefaultTreeCellRenderer;
/**
*
* @author l-ectron-x
*/
class CategoryTreeCellRenderer extends DefaultTreeCellRenderer {
@Override
public Component getTreeCellRendererComponent(
JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
Category category = (Category) value;
setText(category.getName());
return this;
}
}
Ein Hauptknoten
package category;
import static category.Type.PROJECT;
import java.util.ArrayList;
/**
*
* @author l-ectron-x
*/
class Category extends ArrayList<Category> {
private Type type;
private String name;
private Category parent;
Category() {
this(null, "Neues Projekt", PROJECT);
}
Category(Category parent, String name, Type type) {
this.parent = parent;
this.type = type;
this.name = name;
}
public Type getType() {
return type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Category getParent() {
return parent;
}
public boolean isRoot() {
return parent == null;
}
@Override
public String toString() {
return name;
}
}
Ein Spezialknoten:
package category;
/**
*
* @author l-ectron-x
*/
class Page<Image> extends Category {
private String imagePath;
Page() {
}
Page(Category parent, String name, Type type) {
super(parent, name, type);
}
public void setImagePath(String path) {
this.imagePath = path;
}
public String getImagePath() {
return imagePath;
}
}
Der Typ, der vom Spezialknoten gesammelt wird:
package category;
import java.util.Date;
/**
*
* @author l-ectron-x
*/
class Image {
private String name;
Image() {
this.name = "Image_" + String.format("%1$tH:%1$tM:%1$tS", new Date());
}
public String getName() {
return name;
}
}
Eine Aufzählung von Knotentypen, erspart die unnötige Vererbung
package category;
/**
*
* @author l-ectron-x
*/
public enum Type {
PROJECT, SERVER, PART, PAGE, IMAGE
}