I have a java application that uses Swing graphics which has a 2D array of textfields, each of which are updating their text properly throughout the application. On each iteration of change, I change the text of the textfield then I make it's background green for half a second and turn it back to white. The issue is that, after the first iteration of the change, the textfields no longer flash green. When I comment out the portion that converts the background back to white, the rest works and the cells progressively turn green one by one (correctly), which indicates that it is working and executing properly. I tried to address this by repainting and revalidating the UI but it's not working. What is going on?
Below is my code that updates the UI.
try {
textfieldArray[row][col].repaint();
textfieldArray[row][col].revalidate();
textfieldArray[row][col].setBackground(Color.green);
textfieldArray[row][col].repaint();
textfieldArray[row][col].revalidate();
Thread.sleep(300);
textfieldArray[row][col].setBackground(Color.white);
textfieldArray[row][col].repaint();
textfieldArray[row][col].revalidate();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Swing is a single threaded framework, it also NOT thread safe. This means two things.
First, you should never perform any long running or blocking operations within the context of the Event Dispatching Thread and...
Secondly, you should never update/modify the UI, or anything the UI depends on, from outside the context of the Event Dispatching Thread.
This means, your Thread.sleep
is blocking the EDT, preventing from process paint requests.
Instead, you need some way you can trigger an update to occur after a specified delay. If that doesn't scream Swing Timer
, I don't know what does
I would highly recommend taking the time to read through Concurrency in Swing for the background of why your code isn't working and possibly How to Use Swing Timers for a solution
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTextField field;
public TestPane() {
setLayout(new GridBagLayout());
field = new JTextField("All your bases beloging to us", 20);
JButton blink = new JButton("Blink");
Timer timer = new Timer(500, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
field.setForeground(null);
field.setBackground(null);
}
});
timer.setRepeats(false);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(field, gbc);
blink.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
field.setForeground(Color.WHITE);
field.setBackground(Color.GREEN);
timer.start();
}
});
add(blink, gbc);
}
}
}