在 Java 中,我对 JButton 数组使用 ActionListener。我希望 ActionListener 的早期部分将新的 ImageIcon 设置为 JButton,该更改将立即显示,然后在 ActionListener 的末尾附近在第二个长时间延迟后将 JButton 的 ImageIcon 设置回 null。
我的问题是,在 ActionListener 完全完成之前,JButton 发生的任何更改都不会显示在设置它的 GUI 窗口中,从而使 JButton 的 ImageIcon 中的更改不明显。有没有办法让 ActionListener 在完成执行整个 ActionListener 之前向 JButton 提交更改,或者我应该以不同的方式处理此问题?
最佳答案
发生这种情况的原因:
Swing 在运行 ActionListener
时在同一线程 (EDT) 上重新绘制按钮。因此,如果它正在执行您的 ActionListener
它无法重新绘制,因为线程正忙 - 就这么简单。您可能已经注意到,当您的 Action 监听器正在执行时,您也无法正确移动框架等(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);
}
}
}
这里,当 doInBackground()
完成时,在 EDT 上调用 done()
。
关于java - 让 ActionListener 在执行整个 ActionListener 之前向 JButton 提交更改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24435661/