2

我尝试将节点动态添加到 Java SwingJTree中,并且用户应该能够在不断添加节点的同时浏览和折叠层次结构。当我Thread.sleep(10)在循环中添加 a 时,它工作正常;但这是一个肮脏的黑客......

这是触发此问题的精简代码。每当我运行它并双击根节点以展开/折叠它(添加节点时),我都会得到一个ArrayIndexOutOfBoundsException. 当我添加一个Thread.sleep(10)这不会发生。我想这是一个线程问题,但我不知道如何同步?任何提示将不胜感激!

public static void main(String[] args) throws InterruptedException {
    final JFrame frame = new JFrame();
    frame.setSize(600, 800);
    frame.setVisible(true);

    MutableTreeNode root = new DefaultMutableTreeNode("root");
    final DefaultTreeModel model = new DefaultTreeModel(root);
    final JTree tree = new JTree(model);
    frame.add(new JScrollPane(tree));

    while (true) {
        MutableTreeNode child = new DefaultMutableTreeNode("test");
        model.insertNodeInto(child, root, root.getChildCount());
        tree.expandRow(tree.getRowCount() - 1);

        // uncommenting this to make it work
        // Thread.sleep(10);
    }
}

我想将其用于打字搜索应用程序,因此(几乎)提供即时结果对我来说至关重要。

编辑:感谢您的快速回答!SwingUtilities.invokeLater()解决问题。

我现在这样做:

  1. 在其中添加 100 个项目SwingUtilities.invokeLater();
  2. 在 100 个项目之后,我运行它以便更新 GUI:

    // just wait so that all events in the queue can be processed
    SwingUtilities.invokeAndWait(new Runnable() {
        public void run() { }; 
    });
    

这样我就有了一个响应速度非常快的 GUI,而且效果很好。谢谢!

4

3 回答 3

4

tree.expandRow 需要在事件线程中完成,所以改变循环如下:

while (true) 
{
        MutableTreeNode child = new DefaultMutableTreeNode("test");
        model.insertNodeInto(child, root, root.getChildCount());
        final int rowToExpand = tree.getRowCount() - 1; // ? does this work ?
        SwingUtilities.invokeLater(new Runnable()
        {
           public void run()
           {
               tree.expandRow(rowToExpand);
           }
        });

}

当您使用它时,您可能需要确保您的树模型正在使用的任何列表都是同步的,因此您不会在绘制线程遍历树时插入到集合中。

于 2009-01-21T19:39:00.830 回答
0

Swing 是线程敌对的,因此在 AWT 事件 Diaptch 线程 (EDT) 中进行 Swing 操作。

无限循环是无稽之谈,所以很难提出建议。我能想到的最好的等价物是迭代发布一个偶数以再次运行代码。因为事件队列中有某些优先级,所以我不确定这是否有效。

public static void main(String[] args) throws InterruptedException {
    java.awt.EventQueue.invokeLater(new Runnable() { public void run() {
        runEDT();
    }});
}
private static void runEDT() {
    assert java.awt.EventQueue.isDispatchThread();

    final JFrame frame = new JFrame();
    frame.setSize(600, 800);
    frame.setVisible(true);

    final MutableTreeNode root = new DefaultMutableTreeNode("root");
    final DefaultTreeModel model = new DefaultTreeModel(root);
    final JTree tree = new JTree(model);
    frame.add(new JScrollPane(tree));
    frame.validate();

    new Runnable() { public void run() {
        final MutableTreeNode child = new DefaultMutableTreeNode("test");
        model.insertNodeInto(child, root, root.getChildCount());
        tree.expandRow(tree.getRowCount() - 1);

        final Runnable addNode = this; // Inner class confusion...
        java.awt.EventQueue.invokeLater(addNode);
    }}.run();
}

(免责声明:未经编译或测试。)

于 2009-01-21T19:58:18.000 回答
-1

您可能希望从TreeModelListenertreeNodesInserted事件中运行 tree.expandRow 命令,以便它仅在模型更新后运行。

于 2009-01-21T19:27:38.810 回答