Some General Rules
- Swing is not thread safe, you should only ever update UI components from within the context of the Event Dispatching Thread.
- You do not control the paint process, the repaint manager does. You can request updates to occur by calling
repaint
, but you should never call update
and paint
directly when trying to update the display.
- The
Graphics
context used by the paint sub system is a shared resource and is not guaranteed to be the same between paint cycles, you should never maintain a reference to it. You should also not rely on the results from JComponent#getGraphics
this method is capable of returning null.
An Example Solution
You have a number of options, depending on what you want to ultimately achieve.
You could use a SwingWorker
, but given the fact that all your going to is enter an infinite loop and it would easier to use SwingUtilities#invokeLater
then actually use the publish
method, this approach would actually be more work.
You could also use a Thread
, but you'd end up with the same problems as using a SwingWorker
The simpliset solution, for what you're presented, is actually a 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);
}
}
}
You can take a look at Swing Timer and Concurrency in Swing for more info