JTree дает исключение ArrayIndexOutOfBoundsException?

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

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

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
0
1 916
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

Я только что пробовал это сейчас, но получаю точно такой же результат.

martinus 21.01.2009 22:37
Ответ принят как подходящий

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

}

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

Разве вам не нужно продолжить и сохранить tree.getRowCount () - 1, учитывая, что, возможно, было вставлено 50 строк до того, как этот Runnable наконец запустится?

Richard Campbell 21.01.2009 22:44

Swing враждебен потокам, поэтому выполняйте манипуляции с Swing в потоке AWT Event Diaptch Thread (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();
}

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

Другие вопросы по теме