简体   繁体   中英

Swing GUI doesn't wait for user input

I am new to Swing, and I have created a simple GUI class with a button and text field. There is a method in this class, String createAndShowUI() , and I want it to return the text of the text field. I have created another main class that calls this method and expects the text of text field to be returned. However my problem is that this method doesn't wait for the user to enter the text field and click on the button; it returns as soon as GUI is called. I want it to wait for the button click.

// TestSwing.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class TestSwing  extends JPanel implements ActionListener {

    JButton submit;
    JTextField t1;
    String msg = "No Msg";

    public TestSwing() {
        submit = new JButton("Submit");
        t1 = new JTextField(10);
        submit.addActionListener(this);
        setLayout(new FlowLayout());
        add(t1);
        add(submit); 
    }

    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == submit) {
            msg = t1.getText(); 
        }
    }

    public String createAndShowUI() {
        JFrame f = new JFrame("Sample frame");
        f.add(new TestSwing());
        f.pack();
        f.setVisible(true);
        return msg;
    }

}

//Main.java
public class Main {

    public static void main(String[] arg) {
        System.out.println(new TestSwing().createAndShowUI());
    }

}

You're getting your msg String before the user has had a chance to change it, and the reason is that you're thinking in a procedural kind of way, and this won't work for Swing. In fact you've got to change your whole way of thinking in order to code event-driven programming like Swing. So msg isn't shown after a class is created but only after the user has initiated an event -- here the press of a button which prompts an ActionListener to call its actionPerformed method. For example (changes highlighted with the //!! comment):

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

// Significant changes noted with the //!! comment
public class TestSwing extends JPanel implements ActionListener {
   JButton submit;
   JTextField t1;
   String msg = "No Msg";

   public TestSwing() {
      submit = new JButton("Submit");
      t1 = new JTextField(10);
      submit.addActionListener(this);
      setLayout(new FlowLayout());
      add(t1);
      add(submit);

   }

   public void actionPerformed(ActionEvent e) {
      if (e.getSource() == submit) {
         msg = t1.getText();

         //!!  Display msg only **after** the user has pressed enter.
         System.out.println(msg); 
      }

   }

   public void createAndShowUI() { //!! Don't have method return anything
      JFrame f = new JFrame("Sample frame");
      f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //!! close GUI
      f.add(new TestSwing());
      f.pack();
      f.setLocationRelativeTo(null); // center GUI
      f.setVisible(true);
      //!! return msg; // get rid of
   }

   public static void main(String[] arg) {
      new TestSwing().createAndShowUI();
   }

}

Edit 2
You mentioned that you wanted to get the msg text into the main method, or somewhere else other than in the TestSwing class. One way is to use the Observer Design Pattern where you allow other classes to "observe" the TestSwing class -- which is the "observable". There are several ways this can be done, including:

  • Giving TestSwing a public void method called something like addActionListener(ActionListener al) , and in the method body add the listener passed in to the submit button. That way outside classes can add an ActionListener directly to that button and respond to its events.
  • Give TestSwing a way to accept ChangeListeners and notify them if the button has been pressed in its ActionListener, or
  • Give TestSwing the ability to use PropertyChangeListeners by giving it a PropertyChangeSupport variable and public add and remove PropertyChangelistener methods. This is like the ChangeListener idea but since a PCL can listen for multiple state changes, it gives more flexibility and power, and this is currently what I prefer. For example:

Latest version of TestSwing:

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

@SuppressWarnings("serial")
public class TestSwing extends JPanel {
   public static final String MESSAGE = "Message";
   private JButton submit;
   private JTextField mainTextField;
   private String message = "No Msg";

   private PropertyChangeSupport propSupport = new PropertyChangeSupport(this);

   public TestSwing() {
      submit = new JButton("Submit");
      mainTextField = new JTextField(10);
      submit.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            submitActionPerformed(e);
         }
      });
      setLayout(new FlowLayout());
      add(mainTextField);
      add(submit);
   }

   public void addPropertyChangeListener(PropertyChangeListener listener) {
      propSupport.addPropertyChangeListener(listener);
   }

   public void removePropertyChangeListener(PropertyChangeListener listener) {
      propSupport.removePropertyChangeListener(listener);
   }

   public void setMessage(String newValue) {
      String oldValue = message;
      this.message = newValue;
      PropertyChangeEvent event = new PropertyChangeEvent(this, MESSAGE, oldValue, newValue);
      propSupport.firePropertyChange(event);
   }

   private void submitActionPerformed(ActionEvent e) {
      if (e.getSource() == submit) {
         setMessage(mainTextField.getText());
      }
   }

   public static void createAndShowUI() { 
      TestSwing testSwing = new TestSwing();
      testSwing.addPropertyChangeListener(new PropertyChangeListener() {
         public void propertyChange(PropertyChangeEvent evt) {
            if (evt.getPropertyName().equals(TestSwing.MESSAGE)) {
               System.out.println("message = " + evt.getNewValue());
            }
         }
      });

      JFrame f = new JFrame("Sample frame");
      f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
      f.add(testSwing);
      f.pack();
      f.setLocationRelativeTo(null); 
      f.setVisible(true);
   }

   public static void main(String[] arg) {
      createAndShowUI();
   }

}

Please read up on the Observer Design Pattern for more information.

Swing is event-driven. Wherever you want to use msg should be called as a consequence of the action listener being called.

Alternatively you can use one of the ready-made solutions in JOptionPane , for example

String userInput = JOptionPane.showInputDialog("Please enter ...");

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