在Java中,如何在actionPerformed线程当前运行时重新绘制面板?

我有一个类(称为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(); } }; 

假设在Event Dispatching Thread的上下文中调用了actionPerformed方法,直到actionPerformed方法竞争之后才会发生UI更新,即使使用SwingUtilities#invokeLater也不会改变它,因为直到actionPerformed方法退出,EDT将无法继续处理(除其他事项外)重绘请求。

你可以做的最好的事情是启动第二个线程并从该线程中更新你的UI组件……但是,你将被迫使用SwingUtilities#invokeLater因为你不应该更新EDT之外的任何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(); } } } 

您可能希望在AWT和Swing中阅读绘画以获取更多信息。

如果你的actionPerform方法调用代码来更新for循环中的按钮你也可以在invokeLater中添加更新代码,这样更新和绘制代码将逐个运行。 稍后调用将仅在当前方法完成其执行后才执行,因此只有确保绘制更快的方法才能将您的任务分解为更小的peices。

首先,确保您只访问Event Dispatch线程中的任何GUI组件(通过invokeLater或作为处理GUI事件的一部分)。

其次,如果更改GUI组件的任何属性,它应自动发布事件以重新绘制自身。 如果没有,您可以尝试调用component.repaint() 。 但是,在EDT上发生对组件属性的更改至关重要。

一个简单的解决方案是执行整个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 } }); } });