JTree дает ArrayIndexOutOfBoundsException? - PullRequest
2 голосов
/ 21 января 2009

Я пытаюсь динамически добавлять узлы в 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() решает проблему.

Теперь я делаю это:

  1. Добавить 100 пунктов в SwingUtilities.invokeLater();
  2. После 100 пунктов я запускаю это, чтобы графический интерфейс мог обновляться:

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

Таким образом, у меня очень отзывчивый графический интерфейс, и он отлично работает. Спасибо!

Ответы [ 3 ]

4 голосов
/ 21 января 2009

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);
           }
        });

}

Пока вы занимаетесь этим, вам, вероятно, нужно убедиться, что любой список, используемый вашей древовидной моделью, синхронизирован, чтобы вы не вставляли его в коллекцию, пока дерево пересекается потоком краски.

0 голосов
/ 21 января 2009

Swing является враждебным по отношению к потокам, поэтому выполняйте манипуляции с Swing в нити Diaptch AWT (EDT).

Бесконечный цикл - бессмыслица, поэтому трудно придумать предложение. Лучший эквивалент, который я могу придумать, это итерация публикации четного кода для повторного запуска кода. Поскольку в очереди событий есть определенные приоритеты, я не уверен, что это сработает.

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();
}

(Отказ от ответственности: не скомпилировано или протестировано.)

0 голосов
/ 21 января 2009

Возможно, вы захотите запустить команду tree.expandRow из события TreeModelListener * treeNodesInserted, чтобы она запускалась только после обновления модели.

...