WedX - журнал о программировании и компьютерных науках

Почему мне нужно сбросить setText() в JLabel, чтобы предотвратить ошибки?

В ответ на этот вопрос, который я разместил ранее, мне интересно узнать причину проблемы, которая у меня была.

Проблема заключалась в том, что я получал эту ошибку при обновлении JLabel с большим количеством текста HTML.

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at javax.swing.text.html.StyleSheet$ListPainter.paint(Unknown Source)
    at javax.swing.text.html.ListView.paintChild(Unknown Source)
    at javax.swing.text.BoxView.paint(Unknown Source)
    at javax.swing.text.html.BlockView.paint(Unknown Source)
    at javax.swing.text.html.ListView.paint(Unknown Source)
    at javax.swing.text.BoxView.paintChild(Unknown Source)
    at javax.swing.text.BoxView.paint(Unknown Source)
    at javax.swing.text.html.BlockView.paint(Unknown Source)
    at javax.swing.text.BoxView.paintChild(Unknown Source)
    at javax.swing.text.BoxView.paint(Unknown Source)
    at javax.swing.text.html.BlockView.paint(Unknown Source)
    at javax.swing.text.BoxView.paintChild(Unknown Source)
    at javax.swing.text.BoxView.paint(Unknown Source)
    at javax.swing.text.html.BlockView.paint(Unknown Source)
    at javax.swing.plaf.basic.BasicHTML$Renderer.paint(Unknown Source)
    at javax.swing.plaf.basic.BasicLabelUI.paint(Unknown Source)
    at javax.swing.plaf.ComponentUI.update(Unknown Source)
    at javax.swing.JComponent.paintComponent(Unknown Source)
    at javax.swing.JComponent.paint(Unknown Source)
    at javax.swing.JComponent.paintToOffscreen(Unknown Source)
    at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(Unknown Source)
    at javax.swing.RepaintManager$PaintManager.paint(Unknown Source)
    at javax.swing.RepaintManager.paint(Unknown Source)
    at javax.swing.JComponent._paintImmediately(Unknown Source)
    at javax.swing.JComponent.paintImmediately(Unknown Source)
    at javax.swing.RepaintManager$4.run(Unknown Source)
    at javax.swing.RepaintManager$4.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
    at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
    at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
    at javax.swing.RepaintManager.prePaintDirtyRegions(Unknown Source)
    at javax.swing.RepaintManager.access$1200(Unknown Source)
    at javax.swing.RepaintManager$ProcessingRunnable.run(Unknown Source)
    at java.awt.event.InvocationEvent.dispatch(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access$500(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)

Устанавливаемый HTML создается StringBuilder, а преобразованный файл .toString. Что-то вроде этого, но более развернуто:

public static String entityOverviewHTML() {
    StringBuilder sb = new StringBuilder();
    List<Entity> entities = Entity.getEntities();

    sb.append("<html><div style='padding: 12px;'>");

    sb.append("<h1>People: alive</h1>");

    // A lot of appending: also loop through getEntities (dynamic, can change)
    // and get contents from each entity in #getEntityInfo below
    if (entities.size() > 0) {
        sb.append("<ul>");
        for (Entity e: entities) {
            getEntityInfo(e);
        }
        sb.append("</ul>");
    }

    sb.append("</div></html>");
    return sb.toString();
}

private static StringBuilder getEntityInfo(Entity e) {
    StringBuilder sbInfo = new StringBuilder();
    // A lot of appending: append info of Entity e such as e.getFirstName()
    sbInfo.append("<li>");
    sbInfo.append(e.getFirstName())
    sbInfo.append("</li>");

    return sbInfo;
}

После некоторых событий HTML изменится, после чего я вызову собственный метод обновления:

public static void bringToFront() {
    getInstance().setVisible(true);
    getInstance().setExtendedState(JFrame.ICONIFIED);
    getInstance().setExtendedState(JFrame.NORMAL);
}

public static void refresh() {
    // text is a JLabel
    text.setText(entityOverviewHTML());
    bringToFront();
}

И тогда случаются ошибки в верхней части этого поста, однако не всегда! Я не понял, почему это происходит, но обнаружил, что при сбросе текста в пустую строку и последующем вызове entityOverviewHTML проблема решается.

public static void refresh() {
    text.setText(""); // Here we go
    text.setText(entityOverviewHTML());
    bringToFront();
}

text определяется как переменная класса:

private static JLabel text = new JLabel();

Мне хотелось бы знать: почему? Как эта единственная строка, казалось бы, устаревшего кода может решить проблему? В чем именно является проблема?


  • Вы делаете text = new JLabel() где-нибудь в своем коде? 04.01.2016
  • @KevinEsche Смотрите мое редактирование внизу. Да, это переменная класса. 04.01.2016
  • это поможет ? Там нет реального решения, но кажется, что поврежденный html может привести к тому, что плохой парсер html в java создаст полный беспорядок, который в конце концов вызовет ошибку NPE. 04.01.2016
  • @KevinEsche Дело в том, что я не понимаю, как HTML может быть искажен, и если это так, то почему он все еще отображается правильно при первом сбросе содержимого в пустую строку. 04.01.2016
  • я не могу сказать вам точно, почему, но я нашел другую ссылку, которая предоставляет два возможных решения для вашей конкретной проблемы. здесь. Кроме того, похоже, что они его тоже не нашли, а только переустановили свою jre 04.01.2016
  • Для случайных проблем убедитесь, что код выполняется на EDT. Дополнительную информацию см. в разделе Параллелизм в Swing. 04.01.2016
  • вопрос не ясен, и по какой причине(ам) происходит переключение с JFrame.ICONIFIED/NORMAL 04.01.2016
  • @mKorbel переключатель должен сосредоточить внимание на окне. Я получил это из ответа здесь, на SO. Я думаю, что вопрос ясен: почему я не получаю сообщения об ошибке при сбросе JLabel, другими словами: зачем мне нужно сбрасывать JLabel перед изменением его содержимого? 04.01.2016
  • Исключение @Bram Vanroy, говорящее о ... что-то не так с построением синтаксиса Html во время выполнения, затем ..., хммм, вы пытались поместить отформатированный Html (версия = ‹ 3.2) в JEditorPane, вот несколько вопросов о HtmlEditorKit 04.01.2016

Ответы:


1

Проблема в том, что ваш метод refresh() не вызывается в Swing EDT.

Swing не является потокобезопасным (https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html), что означает, что вызов text.setText(entityOverviewHTML()); повреждает внутренние структуры данных, которые ваш экземпляр JLabel использует для отображения текста.

Ваш метод обновления должен быть переписан следующим образом:

public static void refresh() {
    try {
        SwingUtilities.invokeAndWait(new Runnable() {
            public void run() {
                text.setText(entityOverviewHTML());
                bringToFront();
            }
        });
    } catch (InvocationTargetException | InterruptedException e) {
        e.printStackTrace();
    }
}
04.01.2016
  • Думаю, мне это также понадобится в конструкторе моего JFrame. Как мне решить, какая ложь кода входит в метод запуска, а какая нет? Все ли инициализации компонентов и методы входят в этот метод? А как насчет прослушивателей событий. 06.01.2016
  • Да, в основном весь код, который создает или обновляет компоненты Swing, должен выполняться в EDT и вызываться через SwingUtilities.invokeLater()/SwingUtilities.invokeAndWait(). Однако прослушиватели событий не являются проблемой, поскольку они вызываются из свинга и, следовательно, всегда запускаются в EDT. 06.01.2016
  • Спасибо, Томас. Я попробовал это и подумал, что было бы лучше переместить try-catch в конструктор, но это вызывает у меня бесконечный цикл. Если у вас есть время, вы можете посмотреть здесь. 06.01.2016

  • 2

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

    Используйте предпочтительный размер: text.setPreferredSize(new Dimension(x, y));, затем text.setText(html)

    04.01.2016
  • Хотите уточнить это? 04.01.2016
  • Обычно, когда создается экземпляр JLabel, он не имеет ширины, если он создается с пустым текстом, ширина равна нулю. При обновлении текста JLabel он не будет отображаться, поскольку его размер не установлен должным образом. 04.01.2016
  • @svasa Вызывая setText, вы заставляете метку неявно пересчитывать ее preferredSize, это также приведет к повторной проверке и перерисовке родительского контейнера. 04.01.2016
  • Новые материалы

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


    Для любых предложений по сайту: [email protected]