【问题标题】:Make an ActionListener commit a change to a JButton before it has finished executing the entire ActionListener让 ActionListener 在完成整个 ActionListener 执行之前提交对 JButton 的更改
【发布时间】:2014-06-27 00:59:24
【问题描述】:

在 Java 中,我将 ActionListener 用于 JButton 数组。我希望 ActionListener 的早期部分将新的 ImageIcon 设置为 JButton,该更改将立即显示,然后在 ActionListener 的末尾附近将 JButton 的 ImageIcon 在第二个长时间延迟后设置回 null。

我的问题是,在 ActionListener 完全完成之前,JButton 发生的任何更改都不会显示在它设置的 GUI 窗口中,这使得 JButton 的 ImageIcon 中的更改不明显。有没有办法让 ActionListener 在 JButton 完成执行整个 ActionListener 之前提交对它的更改,还是我应该以不同的方式处理?

【问题讨论】:

    标签: java user-interface jbutton actionlistener


    【解决方案1】:

    发生这种情况的原因:

    Swing 在运行 ActionListener 的同一线程 (EDT) 上重新绘制按钮。因此,如果它正在执行你 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);
            }
        }
    }  
    

    doInBackground() 完成后,在 EDT 上调用 done()

    【讨论】:

      【解决方案2】:

      如果你有一个线程池,你可以创建一个新线程或一个线程。然后让任务在单独的线程上工作,以便 ActionListener 立即返回。然后在另一个线程中执行代码并重新绘制按钮。顺便说一句,我不确定它是否会起作用。

      【讨论】:

      • "...在另一个线程中您...重新绘制按钮" 不! 不!切勿在 EDT 之外修改组件!
      • 为什么不呢?它有什么作用?
      • Concurrency in Swing。 (几乎)Swing 中的每个类都有一个很大的注释说 Swing 不是线程安全的。死锁和图形故障就是效果。您会惊讶于有多少简单的函数需要锁定组件树和属性。
      • hmm 很有趣,但如果是这样的话,这将如何被回答。因为主线程在完成任务之前一直处于锁定状态......但是感谢您提供的信息。
      • 我马上写答案,有很多方法可以使用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-03
      • 1970-01-01
      • 1970-01-01
      • 2020-11-01
      • 2012-11-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多