为了创建我的动态JTree,我正在this website in chapter "4.2 OutlineNode.java"
上阅读有关动态JTree的教程现在我已经实现了它并且认识到在GUI线程中加载数据需要很长时间并且也很难看。因此,我添加了一个扩展子节点的线程,然后将TreeNode
元素添加到树中。
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();
}
没有线程它没有问题。如果我添加线程并将add
- 调用放入SwingUtilities.invokeAndWait(...)
,则子项似乎已展开,但它们在树中不可见。
我已经在树上尝试了revalidate()
和repaint()
- 没有任何影响。
知道如何让这些元素可见吗?
提前谢谢。
答案 0 :(得分:3)
检查你的add()方法是否触发了正确的TreeModelEvent
答案 1 :(得分:3)
扩展Walter +1的建议,从JButton Runnable#Thread
调用addButton
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
public class DynamicTreeDemo extends JPanel implements ActionListener {
private static final long serialVersionUID = 1L;
private int newNodeSuffix = 1;
private static String ADD_COMMAND = "add";
private static String REMOVE_COMMAND = "remove";
private static String CLEAR_COMMAND = "clear";
private DynamicTree treePanel;
public DynamicTreeDemo() {
super(new BorderLayout()); // Create the components.
treePanel = new DynamicTree();
populateTree(treePanel);
JButton addButton = new JButton("Add");
addButton.setActionCommand(ADD_COMMAND);
addButton.addActionListener(this);
JButton removeButton = new JButton("Remove");
removeButton.setActionCommand(REMOVE_COMMAND);
removeButton.addActionListener(this);
JButton clearButton = new JButton("Clear");
clearButton.setActionCommand(CLEAR_COMMAND);
clearButton.addActionListener(this); // Lay everything out.
treePanel.setPreferredSize(new Dimension(300, 150));
add(treePanel, BorderLayout.CENTER);
JPanel panel = new JPanel(new GridLayout(0, 3));
panel.add(addButton);
panel.add(removeButton);
panel.add(clearButton);
add(panel, BorderLayout.SOUTH);
}
public void populateTree(DynamicTree treePanel) {
String p1Name = "Parent 1";
String p2Name = "Parent 2";
String c1Name = "Child 1";
String c2Name = "Child 2";
DefaultMutableTreeNode p1, p2;
p1 = treePanel.addObject(null, p1Name);
p2 = treePanel.addObject(null, p2Name);
treePanel.addObject(p1, c1Name);
treePanel.addObject(p1, c2Name);
treePanel.addObject(p2, c1Name);
treePanel.addObject(p2, c2Name);
}
@Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if (ADD_COMMAND.equals(command)) { // Add button clicked
treePanel.addObject("New Node " + newNodeSuffix++);
} else if (REMOVE_COMMAND.equals(command)) { // Remove button clicked
treePanel.removeCurrentNode();
} else if (CLEAR_COMMAND.equals(command)) { // Clear button clicked.
treePanel.clear();
}
}
private static void createAndShowGUI() { // Create and set up the window.
JFrame frame = new JFrame("DynamicTreeDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Create and set up the content pane.
DynamicTreeDemo newContentPane = new DynamicTreeDemo();
newContentPane.setOpaque(true); // content panes must be opaque
frame.setContentPane(newContentPane); // Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createAndShowGUI();
}
});
}
}
class DynamicTree extends JPanel {
private static final long serialVersionUID = 1L;
private DefaultMutableTreeNode rootNode;
private DefaultTreeModel treeModel;
private JTree tree;
private Toolkit toolkit = Toolkit.getDefaultToolkit();
public DynamicTree() {
super(new GridLayout(1, 0));
rootNode = new DefaultMutableTreeNode("Root Node");
treeModel = new DefaultTreeModel(rootNode);
treeModel.addTreeModelListener(new MyTreeModelListener());
tree = new JTree(treeModel);
tree.setEditable(true);
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.setShowsRootHandles(true);
JScrollPane scrollPane = new JScrollPane(tree);
add(scrollPane);
}
public void clear() {
rootNode.removeAllChildren();
treeModel.reload();
}
public void removeCurrentNode() {
TreePath currentSelection = tree.getSelectionPath();
if (currentSelection != null) {
DefaultMutableTreeNode currentNode = (DefaultMutableTreeNode) (currentSelection.getLastPathComponent());
MutableTreeNode parent = (MutableTreeNode) (currentNode.getParent());
if (parent != null) {
treeModel.removeNodeFromParent(currentNode);
return;
}
} // Either there was no selection, or the root was selected.
toolkit.beep();
}
public DefaultMutableTreeNode addObject(Object child) {
DefaultMutableTreeNode parentNode = null;
TreePath parentPath = tree.getSelectionPath();
if (parentPath == null) {
parentNode = rootNode;
} else {
parentNode = (DefaultMutableTreeNode) (parentPath.getLastPathComponent());
}
return addObject(parentNode, child, true);
}
public DefaultMutableTreeNode addObject(DefaultMutableTreeNode parent, Object child) {
return addObject(parent, child, false);
}
public DefaultMutableTreeNode addObject(DefaultMutableTreeNode parent, Object child, boolean shouldBeVisible) {
DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(child);
if (parent == null) {
parent = rootNode;
}
// It is key to invoke this on the TreeModel, and NOT DefaultMutableTreeNode
treeModel.insertNodeInto(childNode, parent, parent.getChildCount());
// Make sure the user can see the lovely new node.
if (shouldBeVisible) {
tree.scrollPathToVisible(new TreePath(childNode.getPath()));
}
return childNode;
}
class MyTreeModelListener implements TreeModelListener {
@Override
public void treeNodesChanged(TreeModelEvent e) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) (e.getTreePath().getLastPathComponent()); /*
* If the event lists children, then the changed node is the child of the
* node we've already gotten. Otherwise, the changed node and the
* specified node are the same.
*/ int index = e.getChildIndices()[0];
node = (DefaultMutableTreeNode) (node.getChildAt(index));
System.out.println("The user has finished editing the node.");
System.out.println("New value NodesChanged: " + node.getUserObject());
}
@Override
public void treeNodesInserted(TreeModelEvent e) {
DefaultMutableTreeNode node= (DefaultMutableTreeNode) (e.getTreePath().getLastPathComponent()); /*
* If the event lists children, then the changed node is the child of the
* node we've already gotten. Otherwise, the changed node and the
* specified node are the same.
*/ int index = e.getChildIndices()[0];
node = (DefaultMutableTreeNode) (node.getChildAt(index));
System.out.println("New value NodesInserted : " + node.getUserObject());
}
@Override
public void treeNodesRemoved(TreeModelEvent e) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) (e.getTreePath().getLastPathComponent()); /*
* If the event lists children, then the changed node is the child of the
* node we've already gotten. Otherwise, the changed node and the
* specified node are the same.
*/ int index = e.getChildIndices()[0];
node = (DefaultMutableTreeNode) (node.getChildAt(index));
System.out.println("New value NodesRemoved : " + node.getUserObject());
}
@Override
public void treeStructureChanged(TreeModelEvent e) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) (e.getTreePath().getLastPathComponent()); /*
* If the event lists children, then the changed node is the child of the
* node we've already gotten. Otherwise, the changed node and the
* specified node are the same.
*/ int index = e.getChildIndices()[0];
node = (DefaultMutableTreeNode) (node.getChildAt(index));
System.out.println("New value StructureChanged : " + node.getUserObject());
}
}
}
答案 2 :(得分:2)
@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);
}
Joude我与否。这适合我。 CustomTreeNode从defaultMutableTreeNode扩展,并添加了一个自写的ChildLoadedListener,当工厂加载子项时调用它。 isExpanded布尔值是为了避免无限循环。 工厂创建一个SwingWorker来加载子进程并执行它。之后调用ChilLoadedListener并再次展开树。
希望这有助于或至少帮助您思考您的问题;-)
编辑:
@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();
}
答案 3 :(得分:1)
我在我正在进行的项目中遇到了同样的问题。
我使用TreeWillExpandListener
来确定何时必须加载树。 (惰性加载)
当树扩展时,我抓住了节点,在线程中加载了它的子节点,因为我必须从服务器输出解析节点。
您面临的问题是树在您的孩子加载之前会扩展。所以你必须扔一个ExpandVetoException
或类似的东西,等到你的孩子被装上。然后扩展你的树。在这种情况下,一切都会正确显示。
希望能够帮助你解决问题。
Exapand-&GT;停止展开 - &gt; loadchildren-&GT; addChildren - &gt;现在展开树 - &gt;看到你的节点
编辑:
如果您使用swing,最好使用SwingWorker。对我来说效果更好。
答案 4 :(得分:0)
尝试将invokeAndWait
替换为invokeLater
。