在Java中,我使用ActionListener作为JButton数组。我希望ActionListener的早期部分将新的ImageIcon设置为JButton,该更改立即显示,然后接近ActionListener的末尾,以便在第二次长时间延迟后将JButton的ImageIcon设置为null
我的问题是,在ActionListener完全完成之前,JButton中发生的任何更改都不会显示在GUI窗口中,这使得JButton的ImageIcon中的更改无法察觉。有没有办法让ActionListener在完成整个ActionListener的执行之前提交对JButton的更改,或者我应该以不同的方式进行此操作?
答案 0 :(得分:2)
发生这种情况的原因:
Swing重新绘制同一线程(EDT)上的按钮,运行ActionListener
。因此,如果它正在执行你ActionListener
,它就无法重绘,因为线程很忙 - 就这么简单。您可能已经注意到,当您的动作监听器正在执行时,您也无法正确移动框架等(GUI冻结)。
解决方案:
在EDT之外移动重型处理。它无论如何也不属于那里。你可能已经猜到了 - 使用后台线程/线程池。一个很好的指南是Swing tutorial for concurrency
注意:
如指南中所描述的那样,您不希望修改EDT之外的组件。因此,最简单的策略是使Runnable
在后台线程上执行,启动它,更改按钮上的图片并返回而不等待任务完成。
public void actionPerformed(ActionEvent ae) {
Runnable task = new Runnable() {..};
executor.execute(task);
button.setIcon(newIcon);
return;
}
请注意,这并不会锁定任务的EDT,因此允许Swing立即更改图片。
这当然意味着用户不知道任务是否已经完成(如果有任何例外)!它毕竟是在后台!因此,执行会有一个额外的状态:GUI响应和非冻结,按钮被更改,但任务仍在运行。在大多数应用程序中,这可能是一个问题(用户将垃圾邮件按钮或您的后台任务可能交错)。在这种情况下,您可能希望使用SwingWorker
进行"处理"国家也是如此。
public void actionPerformed(ActionEvent ae) {
new TaskWorker().execute();
button.setIcon(loadingIcon); //Shows loading. Maybe on button, maybe somewhere else.
return;
}
private class TaskWorker extends SwingWorker<Void, T> {
public Void doInBackground() {
//Do your task in the background here
}
protected void done() {
try {
get();
button.setIcon(doneIcon);
catch (<relevant exceptions>) {
button.setIcon(failedIcon);
}
}
}
当done()
完成时,doInBackground()
在EDT上被称为 。
答案 1 :(得分:0)
如果您有线程池,则可以从线程池创建新的线程或线程。然后让任务在一个单独的Thread上运行,以便ActionListener立即返回。然后在另一个线程中,您执行代码并重新绘制按钮。顺便说一下,我不确定它是否会起作用。