Swing按钮不能立即响应!我该如何更改?

3
我使用Netbeans的可视化编辑器构建了一个表单。当我按下其中一个按钮时,它应该执行以下操作:
  • 将其设置为禁用状态
  • 执行需要一些时间的任务
  • 当任务完成后,按钮将再次启用
然而,以下情况发生:
  • 按钮会保持按下状态直到任务完成
  • 当任务完成时,启用/禁用按钮会非常快(它们会发生,但您不会注意到它们)
这种行为不是我想要的。我尝试在JButton、JFrame甚至包含按钮的JPanel上使用repaint,但我似乎无法让它做我想做的事情。有什么提示吗?
4个回答

13

当您在按钮回调函数中执行任务时,会阻塞GUI绘制线程直到任务完成。

您需要做的是启动一个线程来执行长时间运行的任务,然后让该线程在完成时使用SwingUtilities.invokeLater() 更新UI。不使用invokeLater 不是线程安全的,并且通常不是好的做法。

一个基本示例:

button.setEnabled(false);
new Thread(new Runnable() {
 public void run() {
  // Do heavy lifting here
  SwingUtilies.invokeLater(new Runnable() {
   public void run() {
    button.setEnabled(true);
   }
  });
 }
}).start();

“button.setEnabled(true)” 部分是在执行任务的线程中进行的吗?如果我不想使用匿名类,我应该将按钮传递给线程吗? - Geo
它正在GUI线程中进行。请参见http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/SwingUtilities.html#invokeLater(java.lang.Runnable)。 - Allain Lalonde
至于你有多讨厌匿名类,那就由你自己决定了。 - Allain Lalonde
你可能也想在那里加上一个try-finally。此外,通过在EDT中计算新的按钮状态而不是使用关于位置的旧状态来触发延迟消息更加健壮。 - Tom Hawtin - tackline
这个答案缺乏解释 - 可以从我的答案中复制一些解释。 - Paul Tomblin
显示剩余2条评论

8
当您在按钮回调中执行操作时,实际上是阻塞了GUI绘制线程 - 不仅仅是按钮,而是任何GUI绘制。(尝试用另一个窗口覆盖界面,然后再次显示它 - 直到任务完成它才会重新绘制!)
您需要做的是生成一个线程来执行长时间运行的任务,然后让该线程使用SwingUtilities.invokeLater()来启用按钮。invokeLater强制按钮启用在GUI绘图线程中发生。
在长时间运行的线程正在操作时,您可能需要设置忙碌光标或以其他方式锁定界面。

很遗憾,我们无法合并答案。 - Allain Lalonde
@Allain,重要的是确保被接受的答案尽可能地好。 - Paul Tomblin

4

Swing中的并发性教程来自Sun,非常值得一读。其中包括事件分派线程、使用工作线程等方面的优秀解释和背景知识。


2

你需要在不同的线程中完成需要耗费一些时间的任务。

按钮阻塞的原因是因为工作正在绘制按钮的同一线程中进行。一旦任务完成,按钮就能够执行剩下的操作了。

如果你使用一个不同的线程,那么该线程会在绘制代码继续绘制表单的同时执行任务。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接