问题
I have a class (called Class_GUI) which has a panel with lots of buttons on it. Class_GUI has some methods that change the text and colour of the buttons.
I have also have a program with the actionPerformed method. When this is called it creates an instance of Class_GUI and repeatedly calls Class_GUI methods, changing the buttons etc.
The issue I'm having is that the buttons only display properly once the actionPerformed method has finished entirely whereas I want it to change after each Class_GUI method is called.
My attempt so far is in each Class_GUI method I do this at the end of the method:
SwingUtilities.invokeLater(Refresh_GUI);
Where Refresh_GUI is defined:
Runnable Refresh_GUI = new Runnable(){
public void run(){
frame.revalidate();
frame.repaint();
}
};
回答1:
Assuming that your actionPerformed
method is being called within the context of the Event Dispatching Thread, no UI updates will occur until AFTER the actionPerformed
method has competed, even using SwingUtilities#invokeLater
won't change that, because until the actionPerformed
method exits, the EDT won't be able to continue processing (amongst other things) repaint requests.
The best you can do, is start a second thread and from within that thread, update your UI components...but, you area going to be forced to use SwingUtilities#invokeLater
as you should NEVER update any UI component outside the EDT.
The advantage though, is that the thread does not need to compete in order for the EDT to start processing the repaint request
UPDATED with Example
public class SwingThreadUpdate {
public static void main(String[] args) {
new SwingThreadUpdate();
}
public SwingThreadUpdate() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new BlinkPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BlinkPane extends JPanel {
private JLabel label;
private JButton button;
public BlinkPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = 0;
label = new JLabel("Blinky");
label.setBackground(Color.RED);
button = new JButton("Click me");
add(label, gbc);
gbc.gridy++;
add(button, gbc);
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
button.setEnabled(false);
new Thread(new BlinkTask(BlinkPane.this)).start();
}
});
}
private void setBlink(boolean blink) {
label.setOpaque(blink);
}
private void reset() {
button.setEnabled(true);
label.setOpaque(false);
}
}
public class BlinkTask implements Runnable {
private BlinkPane blinkPane;
protected BlinkTask(BlinkPane blinkPane) {
this.blinkPane = blinkPane;
}
@Override
public void run() {
Blink blinkOn = new Blink(blinkPane, true);
Blink blinkOff = new Blink(blinkPane, false);
for (int index = 0; index < 10; index++) {
if (index % 2 == 0) {
SwingUtilities.invokeLater(blinkOn);
} else {
SwingUtilities.invokeLater(blinkOff);
}
try {
Thread.sleep(125);
} catch (InterruptedException ex) {
}
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
blinkPane.reset();
}
});
}
}
public class Blink implements Runnable {
private BlinkPane blinkPane;
private boolean blink;
public Blink(BlinkPane blinkPane, boolean blink) {
this.blinkPane = blinkPane;
this.blink = blink;
}
@Override
public void run() {
blinkPane.setBlink(blink);
blinkPane.repaint();
}
}
}
You might like to have a read through Painting in AWT and Swing for more information.
回答2:
Incase your actionPerform method calls code to update buttons in a for loop you could also add the updation code in the invokeLater that way both the updation and painting code will run one by one. Invoke later will execute only after current method completes its execution so only way to ensure painting happens faster is to break your tasks into smaller peices.
回答3:
First, make sure you are only accessing any GUI components from the Event Dispatch thread (via invokeLater or as part of handling a GUI event).
Second, if you change any properties of a GUI component, it should automatically post an event to repaint itself. If not you can try invoking component.repaint()
. But it's critical that the changes to the component properties happen on the EDT.
回答4:
A simple solution is execute the entire ActionPerformed event less task to clean the screen at the end of the event queue. So, first it executes the cleanScreen() function because the rest of the event waits for all events finish.
AnyButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
cleanScreen(); //Modify components before action performer event
EventQueue.invokeLater( new Runnable() {
@Override public void run() {
anytask(); //Action performer event
}
});
}
});
来源:https://stackoverflow.com/questions/13750659/in-java-how-do-i-repaint-a-panel-from-an-actionperformed-thread-while-it-is-curr