To create my dynamic JTree, I was reading the tutorial about Dynamic JTrees on this website in chapter "4.2 OutlineNode.java"
Now I have implemented it and recognize that loading the data in the GUI thread takes long and is ugly as well. Therefore I added a thread which expands the children and then adds the TreeNode
element to the tree.
private void getChildNodes() {
areChildrenDefined = true;
Thread t = new Thread(new Runnable()
{
@Override
public void run() {
System.out.println("Expand");
final List<DECTTreeNode> listNodes = new ArrayList<DECTTreeNode>();
if (castNode().canExpand())
{
for(DECTNode crt : castNode().getChildren())
{
DECTTreeNode treeNode = new DECTTreeNode(crt);
listNodes.add(treeNode);
}
try {
SwingUtilities.invokeAndWait(new Runnable()
{
@Override
public void run() {
System.out.println(listNodes.size());
for (DECTTreeNode crt : listNodes)
{
add(crt); // <==== Adds the node to the JTree
}
}
});
//}).run();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
t.start();
}
Without the thread it works without problems. If I add the thread and put the add
-calls into a SwingUtilities.invokeAndWait(...)
, the children seem to be expanded, but they're not visible in the tree.
I've already tried revalidate()
and repaint()
on the tree - without any effect.
Any idea how to make these elements visible?
Thank you in advance.
@Override
public void treeWillExpand(TreeExpansionEvent e) throws ExpandVetoException {
CustomTreeNode t = (CustomTreeNode) e.getPath().getLastPathComponent();
t.addChildLoadedListener(new ChildLoadedListener() {
@Override
public void childLoaded(TreeNode parent) {
((CustomTreeNode) parent).setExpanded(true);
expandPath(new TreePath(((CustomTreeNode) parent).getPath()));
}
});
if (!t.isExpanded()) {
factory.loadChildren(t);
throw new ExpandVetoException(null);
}
}
public void treeWillCollapse(TreeExpansionEvent e) throws ExpandVetoException {
CustomTreeNode t = (CustomTreeNode) e.getPath().getLastPathComponent();
t.setExpanded(false);
}
Jugde me or not. This works for me. CustomTreeNode is extended from defaultMutableTreeNode and has been added a selfwritten ChildLoadedListener which is called when the children have been loaded by the factory. The isExpanded boolean is to avoid a endless loop. the factory creates a SwingWorker that loads the children and executes it. after that the ChilLoadedListener is called and the tree is expanded again.
Hope this will help or at least help you think about your problem ;-)
EDIT :
@Override
public void loadChildren(CustomTreeNode tn) {
ctn = tn;
LoadChildrenWorker worker = new LoadChildrenWorker();
worker.execute();
}
private class LoadChildrenWorker extends SwingWorker<String, Object> {
@Override
protected String doInBackground() throws Exception {
//load source here and return a string when finished.
//In my case its a string repesentation of a directory
}
@Override
protected void done() {
//with get(), you get the string from doBackground()
for (String str : parseFromOutput(get())) {
if (str.endsWith("/")) {
ctn.add(new CustomTreeNode("Directory");
} else {
ctn.add(new CustomTreeNode("Leaf");
}
}
//call listeners
ctn.fireChildrenLoaded();
}