Java中的操作优先级。(在GUI更新之前,对象实例化并运行?)

Java中的操作优先级。(在GUI更新之前,对象实例化并运行?),java,multithreading,swing,user-interface,Java,Multithreading,Swing,User Interface,我希望GUI在实例化对象并实际执行操作之前,将按钮的标题从“Go”更改为“Working…”。完成后,我希望按钮的标题切换回“Go” 代码如下: private class convert implements ActionListener { public void actionPerformed(ActionEvent e) { JButton button = (JButton)e.getSource(); button.setText("

我希望GUI在实例化对象并实际执行操作之前,将按钮的标题从“Go”更改为“Working…”。完成后,我希望按钮的标题切换回“Go”

代码如下:

    private class convert implements ActionListener {
    public void actionPerformed(ActionEvent e) {
        JButton button = (JButton)e.getSource();

        button.setText("Working...");
        button.setEnabled(false);

        anObject name = new AnObject();
        boolean result = name.methodName(chooser.getSelectedFile(),encoding);

        // A bunch of stuff was here but irrelevant to the question,
        // so it was removed to save room.

        button.setEnabled(true);
        button.setText("Go");
    }
实际上,在实践中发生的是名称被实例化,methodName被调用,然后按钮在屏幕上得到更新,尽管我已经告诉VM首先更改按钮标题

我的工作原理是,考虑到我没有使这个程序线程化,这与操作优先级、JVM的内部线程化或其他有关

有什么建议吗?

事件线程中会出现一个“ActionPerformed”回调,在返回之前,GUI上通常不会更新任何内容。如果您想更新gui,做一些事情,然后再次更新它,您需要更新gui并生成一个线程并返回。然后线程必须执行它的操作,然后执行SwingUtilities.invokeLater来更新gui。

试试看


我知道您已经接受了一个解决方案,但由于您遇到了“冻结gui”综合症,您肯定会遇到线程问题,invokeLater无法解决您的问题。正如上面extraneon提到的,您需要一个SwingWorker或一些后台线程来解决这个问题。另外,我认为这是一个使用AbstractAction而不是ActionListener的好例子。例如:

import java.awt.Dimension;
import java.awt.event.*;
import javax.swing.*;

@SuppressWarnings("serial")
public class Convert extends AbstractAction {
   private static final long SLEEP_TIME = 3000; // 3 seconds
   private String enabledText;
   private String disabledText;

   public Convert(String enabledText, String disabledText) {
      super(enabledText);
      this.enabledText = enabledText;
      this.disabledText = disabledText;
   }

   public void actionPerformed(ActionEvent e) {
      Object source = e.getSource();
      if (!(source instanceof JButton)) {
         return;
      }
      final JButton button = (JButton) source;
      setButtonEnabled(button, false);
      new SwingWorker<Void, Void>() {
         @Override
         protected Void doInBackground() throws Exception {
            // TODO: long-running code goes here. 
            // Emulated by Thread.sleep(...) 
            Thread.sleep(SLEEP_TIME);
            return null;
         }

         @Override
         protected void done() {
            setButtonEnabled(button, true);
         }
      }.execute();
   }

   public void setButtonEnabled(JButton button, boolean enabled) {
      if (enabled) {
         button.setText(enabledText);
         button.setEnabled(true);
      } else {
         button.setText(disabledText);
         button.setEnabled(false);
      }
   }

   private static void createAndShowUI() {
      JFrame frame = new JFrame("Convert");
      frame.getContentPane().add(new ConvertGui());
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      java.awt.EventQueue.invokeLater(new Runnable() {
         public void run() {
            createAndShowUI();
         }
      });
   }
}

@SuppressWarnings("serial")
class ConvertGui extends JPanel {
   public ConvertGui() {
      add(new JButton(new Convert("GO", "Working...")));
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(300, 200);
   }
}
导入java.awt.Dimension;
导入java.awt.event.*;
导入javax.swing.*;
@抑制警告(“串行”)
公共类转换扩展了抽象操作{
私有静态最终长睡眠时间=3000;//3秒
启用私有字符串的文本;
私有字符串禁用文本;
公共转换(字符串启用文本、字符串禁用文本){
超级(启用文本);
this.enabledText=enabledText;
this.disabledText=disabledText;
}
已执行的公共无效操作(操作事件e){
对象源=e.getSource();
if(!(JButton的源实例)){
返回;
}
最终JButton按钮=(JButton)源;
setButtonEnabled(按钮,错误);
新SwingWorker(){
@凌驾
受保护的Void doInBackground()引发异常{
//TODO:长期运行的代码在这里。
//由Thread.sleep(…)模拟
睡眠(睡眠时间);
返回null;
}
@凌驾
受保护的void done(){
setButtonEnabled(按钮,真);
}
}.execute();
}
public void setButtonEnabled(JButton按钮,布尔值启用){
如果(已启用){
按钮。设置文本(启用文本);
按钮。setEnabled(真);
}否则{
按钮。设置文本(禁用文本);
按钮。设置启用(错误);
}
}
私有静态void createAndShowUI(){
JFrame=新JFrame(“转换”);
frame.getContentPane().add(新的ConvertGui());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(空);
frame.setVisible(true);
}
公共静态void main(字符串[]args){
invokeLater(new Runnable()){
公开募捐{
createAndShowUI();
}
});
}
}
@抑制警告(“串行”)
类ConvertGui扩展了JPanel{
公共GUI(){
添加(新的JButton(新的Convert(“GO”,“Working…”));
}
@凌驾
公共维度getPreferredSize(){
返回新维度(300200);
}
}

请注意,这将在EDT中运行-如果methodName花费很长时间,您的应用程序将冻结。如果您有一个长期运行的作业,请使用SwingWorker,并更新done()方法中的标题栏。这就解决了问题,谢谢!对Java来说相当陌生,所以我仍然在学习一些怪癖,比如这个。@extraneon-也谢谢。鉴于操作的长度取决于methodName处理的文件的长度,我可能需要实现您的建议。我想知道“长期工作”需要多长时间……;)-这是错误的。在本例中,当您使用
SwingUtilities
@Moonbeam时,您已经处于
EDT
中:如果从事件调度线程调用invokeLater(例如,从JButton的ActionListener调用),则doRun.run()仍将被延迟,直到处理完所有挂起的事件。请注意,如果doRun.run()抛出未捕获的异常,则事件调度线程将展开(而不是当前线程)。感谢您的解释。这是上面建议的代码的一个不错的附加组件,并解释了为什么需要它。:)另一个可能的解决方案是,如果您的“一堆无关的东西…”涉及显示另一个窗口,并且您使用的是JFrame,请使用模态JDialog。另外,请参阅我发布的SwingWorker和AbstractAction解决方案。+1,是的!我不明白为什么OP认为公认的答案是正确的!不是!如果从事件调度线程(例如,从JButton的ActionListener)调用invokeLater,doRun.run()仍将被延迟,直到处理完所有挂起的事件。请注意,如果doRun.run()抛出未捕获的异常,则事件调度线程将展开(而不是当前线程)。我接受了答案,因为在实现解决方案时,它起到了作用。也就是说,如果它有问题,那么最好知道;正如extraneon在评论中所说,我没有说user802421的答案是错误的,事实上,在发表这篇文章之前,我对它投了赞成票。我想说的是,如果你有一个冻结的GUI,那么你就有一个线程问题,需要使用后台线程。编辑:正如Paul Tomblin指出的——1+代表他的答案:Swing大师之一珍妮特(kleopatra)说,你应该总是使用动作,而不是动作听众,但我不记得她的理由。我使用行动的动机是你
import java.awt.Dimension;
import java.awt.event.*;
import javax.swing.*;

@SuppressWarnings("serial")
public class Convert extends AbstractAction {
   private static final long SLEEP_TIME = 3000; // 3 seconds
   private String enabledText;
   private String disabledText;

   public Convert(String enabledText, String disabledText) {
      super(enabledText);
      this.enabledText = enabledText;
      this.disabledText = disabledText;
   }

   public void actionPerformed(ActionEvent e) {
      Object source = e.getSource();
      if (!(source instanceof JButton)) {
         return;
      }
      final JButton button = (JButton) source;
      setButtonEnabled(button, false);
      new SwingWorker<Void, Void>() {
         @Override
         protected Void doInBackground() throws Exception {
            // TODO: long-running code goes here. 
            // Emulated by Thread.sleep(...) 
            Thread.sleep(SLEEP_TIME);
            return null;
         }

         @Override
         protected void done() {
            setButtonEnabled(button, true);
         }
      }.execute();
   }

   public void setButtonEnabled(JButton button, boolean enabled) {
      if (enabled) {
         button.setText(enabledText);
         button.setEnabled(true);
      } else {
         button.setText(disabledText);
         button.setEnabled(false);
      }
   }

   private static void createAndShowUI() {
      JFrame frame = new JFrame("Convert");
      frame.getContentPane().add(new ConvertGui());
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      java.awt.EventQueue.invokeLater(new Runnable() {
         public void run() {
            createAndShowUI();
         }
      });
   }
}

@SuppressWarnings("serial")
class ConvertGui extends JPanel {
   public ConvertGui() {
      add(new JButton(new Convert("GO", "Working...")));
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(300, 200);
   }
}