一些一般规则
- Swing 不是线程安全的,您应该只在 Event Dispatching Thread 的上下文中更新 UI 组件。
- 您无法控制绘制过程,重绘管理器可以。您可以通过调用 来请求更新,但在尝试更新显示时
repaint
切勿直接调用update
and 。paint
Graphics
绘制子系统使用的上下文是共享资源,不能保证在绘制周期之间是相同的,你永远不应该维护对它的引用。您也不应该依赖JComponent#getGraphics
此方法的结果能够返回 null。
示例解决方案
您有多种选择,具体取决于您最终想要实现的目标。
你可以使用 a SwingWorker
,但考虑到你要做的就是进入一个无限循环并且它更容易使用SwingUtilities#invokeLater
然后实际使用该publish
方法,这种方法实际上会更有效。
您也可以使用 a Thread
,但最终会遇到与使用 a 相同的问题SwingWorker
对于您所呈现的,最简单的解决方案实际上是javax.swing.Timer
public class Blinky {
public static void main(String[] args) {
new Blinky();
}
public Blinky() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new BlinkyPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
protected class BlinkyPane extends JPanel {
private JLabel blinkyLabel;
private boolean blink = false;
public BlinkyPane() {
setLayout(new GridBagLayout());
blinkyLabel = new JLabel("I'm blinking here");
blinkyLabel.setBackground(Color.RED);
add(blinkyLabel);
Timer timer = new Timer(250, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
blink = !blink;
if (blink) {
blinkyLabel.setForeground(Color.YELLOW);
} else {
blinkyLabel.setForeground(Color.BLACK);
}
blinkyLabel.setOpaque(blink);
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 100);
}
}
}
您可以查看 Swing 中的Swing 计时器和并发以获取更多信息