为什么我需要在 JLabel 中重置 setText() 以防止错误?
Why do I need to reset setText() in a JLabel to prevent errors?
作为我之前 post 编辑的 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.run(Unknown Source)
at javax.swing.RepaintManager.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.access00(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.access0(Unknown Source)
at java.awt.EventQueue.run(Unknown Source)
at java.awt.EventQueue.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();
}
然后 post 顶部的错误就会发生,但是 并非总是如此!我还没有弄清楚为什么会这样,但我确实发现将文本重置为空字符串然后调用 entityOverviewHTML 可以解决问题。
public static void refresh() {
text.setText(""); // Here we go
text.setText(entityOverviewHTML());
bringToFront();
}
文本定义为 Class 变量:
private static JLabel text = new JLabel();
我想知道的是:为什么?这一行看似过时的代码如何解决问题? 究竟是什么问题?
通常在实例化JLabel 时,它没有宽度,如果实例化为空文本宽度为零。当更新 JLabel 文本时,它不会显示,因为它的大小设置不正确。
使用首选尺寸:text.setPreferredSize(new Dimension(x, y));
然后 text.setText(html)
问题是您的 refresh()
方法没有在 Swing EDT 上调用。
Swing 不是线程安全的 (https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html),这意味着调用 text.setText(entityOverviewHTML());
会破坏 JLabel 实例用来显示文本的内部数据结构。
你的refresh-method需要这样重写:
public static void refresh() {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
text.setText(entityOverviewHTML());
bringToFront();
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
}
作为我之前 post 编辑的 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.run(Unknown Source)
at javax.swing.RepaintManager.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.access00(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.access0(Unknown Source)
at java.awt.EventQueue.run(Unknown Source)
at java.awt.EventQueue.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();
}
然后 post 顶部的错误就会发生,但是 并非总是如此!我还没有弄清楚为什么会这样,但我确实发现将文本重置为空字符串然后调用 entityOverviewHTML 可以解决问题。
public static void refresh() {
text.setText(""); // Here we go
text.setText(entityOverviewHTML());
bringToFront();
}
文本定义为 Class 变量:
private static JLabel text = new JLabel();
我想知道的是:为什么?这一行看似过时的代码如何解决问题? 究竟是什么问题?
通常在实例化JLabel 时,它没有宽度,如果实例化为空文本宽度为零。当更新 JLabel 文本时,它不会显示,因为它的大小设置不正确。
使用首选尺寸:text.setPreferredSize(new Dimension(x, y));
然后 text.setText(html)
问题是您的 refresh()
方法没有在 Swing EDT 上调用。
Swing 不是线程安全的 (https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html),这意味着调用 text.setText(entityOverviewHTML());
会破坏 JLabel 实例用来显示文本的内部数据结构。
你的refresh-method需要这样重写:
public static void refresh() {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
text.setText(entityOverviewHTML());
bringToFront();
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
}