java - 为什么需要在 JLabel 中重置 setText() 以防止错误?

标签 java swing jlabel stringbuilder

作为 this question 的后续行动我之前发布过,我想知道我遇到的问题的原因。

问题是我在更新包含大量 HTML 文本的 JLabel 时遇到此错误。

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

文本被定义为类变量:

private static JLabel text = new JLabel();

我想知道的是:为什么?这行看似过时的代码如何解决问题呢?问题到底是什么?

最佳答案

问题是你的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();
    }
}

关于java - 为什么需要在 JLabel 中重置 setText() 以防止错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34593471/

相关文章:

java - 如何更新 JPanel 的内容?

java - 具有最大组合值的链接 JSlider

java - 如何获取MVC计算器java上显示的数字?

java - 将图像渲染到标签中

java - 使用 NetBeans 在 SQL 数据库中将日期保存为 dd/MM/yyyy

java - 图片和文字在同一个窗口

java - 获取使用 Java 构建 Windows 版本

java - 小程序 - java.awt.awtpermission accessclipboard

Java统计字符串中数字、特殊字符和字母的变化次数

java - 关于 JTextField 和带数字的文本格式的问题?