いくつかの一般規則
- Swing はスレッド セーフではありません。イベント ディスパッチ スレッドのコンテキスト内からのみ UI コンポーネントを更新する必要があります。
- ペイント プロセスはユーザーが制御するのではなく、再ペイント マネージャーが制御します。を呼び出して更新を要求することはできますが、表示を更新しようとするときに直接andを
repaint
呼び出さないでください。update
paint
- ペイント サブシステムによって使用される
Graphics
コンテキストは共有リソースであり、ペイント サイクル間で同じであることが保証されていないため、参照を保持しないでください。JComponent#getGraphics
また、このメソッドの結果が null を返すことに依存しないでください。
ソリューション例
最終的に何を達成したいかによって、いくつかのオプションがあります。
を使用することもできますがSwingWorker
、無限ループに入ることがすべてであり、SwingUtilities#invokeLater
実際にpublish
メソッドを使用するよりも使いやすいという事実を考えると、このアプローチは実際にはより多くの作業になります。
を使用することもできますが、を使用しThread
た場合と同じ問題が発生します。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 TimerとConcurrency を参照してください。