我尝试将节点动态添加到Java Swing JTree
,并且用户应该能够在不断添加节点时浏览和折叠层次结构。当我在循环中添加Thread.sleep(10)
时,它工作正常;但这是一个肮脏的黑客...
以下是触发此问题的精简代码。每当我运行它并双击根节点以展开/折叠它(添加节点时),我得到一个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()
解决了这个问题。
我现在这样做:
SwingUtilities.invokeLater();
在100个项目之后,我运行它以便GUI可以更新:
// just wait so that all events in the queue can be processed
SwingUtilities.invokeAndWait(new Runnable() {
public void run() { };
});
这样我有一个非常敏感的GUI,它完美地工作。谢谢!
答案 0 :(得分: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);
}
});
}
当你在它的时候,你可能需要确保你的树模型正在使用的任何列表是同步的,所以你不要在绘制线程遍历树时插入到集合中。
答案 1 :(得分: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();
}
(免责声明:未编译或测试。)
答案 2 :(得分:-1)
您可能希望从TreeModelListener的treeNodesInserted
事件运行tree.expandRow命令,以便它仅在模型更新后运行。