简体   繁体   中英

Operation priority in Java. (An object instantiates and runs before the GUI is updated?)

I want the GUI to change the title of a button from "Go" to "Working..." before an object is instantiated and actually does the work. When finished, I want the title of the button to switch back to "Go."

Here's the code:

    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");
    }

What actually happens in practise is name is instantiated, methodName gets called, and THEN the button gets updated on the screen, despite the fact that I have told the VM to change the button title first.

My working theory is, given I have not made this program threaded, this has something to do with operational priority, or internal threading of the JVM, or something...

Any suggestions?

I know that you've already accepted a solution, but since you are running into the "frozen gui" syndrome, you most definitely have a threading issue, and invokeLater won't solve your problem. As noted above by extraneon, you need a SwingWorker or some background thread to solve this. Also, I think that this is a good case for use of an AbstractAction rather than an ActionListener. For example:

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);
   }
}

An "ActionPerformed" callback occurs in the event thread, and generally nothing on the GUI will update until it returns. If you want to update the gui, do something, and then update it again, you will need to update the gui and spawn a thread and return. The thread will then have to do it's action, and then do a SwingUtilities.invokeLater to update the gui.

Try SwingUtilities.invokeLater() .

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

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

    javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            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("Convert");
        }
    });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM