我有一个类(称为Class_GUI),它有一个面板上有很多按钮。 Class_GUI有一些方法可以改变按钮的文本和颜色。
我还有一个带有actionPerformed方法的程序。调用它时,它会创建一个Class_GUI实例,并重复调用Class_GUI方法,更改按钮等。
我遇到的问题是,一旦actionPerformed方法完全完成,按钮只能正常显示,而我希望在调用每个Class_GUI方法后更改按钮。
到目前为止,我的尝试是在每个Class_GUI方法中,我在方法结束时执行此操作:
SwingUtilities.invokeLater(Refresh_GUI);
定义了Refresh_GUI:
Runnable Refresh_GUI = new Runnable(){
public void run(){
frame.revalidate();
frame.repaint();
}
};
答案 0 :(得分:3)
假设您在事件调度线程的上下文中调用了actionPerformed
方法,在actionPerformed
方法竞争之后,即使使用SwingUtilities#invokeLater
won',也不会发生UI更新改变这一点,因为在actionPerformed
方法退出之前,EDT将无法继续处理(除其他事项外)重绘请求。
您可以做的最好的事情是,启动第二个线程,并从该线程中更新您的UI组件......但是,您将被迫使用SwingUtilities#invokeLater
区域,因为您永远不应更新任何UI组件在美国东部时间之外。
优点是,线程不需要竞争,以便EDT开始处理重绘请求
使用示例更新
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();
}
}
}
您可能希望阅读Painting in AWT and Swing以获取更多信息。
答案 1 :(得分:2)
如果你的actionPerform方法调用代码来更新for循环中的按钮,你也可以在invokeLater中添加更新代码,这样更新和绘制代码将逐个运行。稍后调用将仅在当前方法完成其执行后执行,因此只有确保绘制更快的方法才能将您的任务分解为更小的peices。
答案 2 :(得分:1)
首先,确保您只访问Event Dispatch线程中的任何GUI组件(通过invokeLater或作为处理GUI事件的一部分)。
其次,如果更改GUI组件的任何属性,它应自动发布事件以重新绘制自身。如果没有,您可以尝试调用component.repaint()
。但是,对于组件属性的更改发生在EDT上至关重要。
答案 3 :(得分:0)
一个简单的解决方案是执行整个ActionPerformed事件减少任务,以清理事件队列末尾的屏幕。 因此,首先它执行cleanScreen()函数,因为事件的其余部分等待所有事件完成。
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
}
});
}
});