Swing GUI不等待用户输入。

3

我刚接触Swing,创建了一个简单的GUI类,其中包含一个按钮和文本字段。在这个类中有一个方法String createAndShowUI(),我想让它返回文本字段的文本。我创建了另一个主类调用这个方法,并期望返回文本字段的文本。但是我的问题是,这个方法不会等待用户输入文本并单击按钮;它会在GUI被调用时立即返回。我希望它等待按钮点击。

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

}
2个回答

6

您在用户有机会更改消息字符串之前就获取了它,原因是您按照过程化的方式进行思考,这对Swing不起作用。实际上,为了编写像Swing一样的事件驱动编程,您必须改变整个思维方式。因此,在创建类之后,并不会显示msg,只有在用户发起事件之后才会显示--例如按下按钮,这会提示ActionListener调用其actionPerformed方法。例如(使用//!!注释突出显示所做的更改):

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

}

编辑2
您提到希望将“msg text”放入主方法或其他地方而非TestSwing类中。一种方法是使用观察者模式,允许其他类"观察"TestSwing类——即"可观察"的类。有几种实现方式,包括:

  • 为TestSwing添加一个名为addActionListener(ActionListener al)的公共void方法,并在方法体中向提交按钮添加传入的监听器。这样外部类就可以直接将ActionListener添加到该按钮并响应其事件。
  • 为TestSwing提供一种接受ChangeListeners并在其ActionListener中通知它们的方法,或
  • 通过为TestSwing提供PropertyChangeSupport变量和public add和remove PropertyChangelistener方法来使用PropertyChangeListeners。这与ChangeListener思路类似,但由于PCL可以监听多个状态更改,因此具有更大的灵活性和强大性,这是我目前更喜欢的方法。例如:

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

}

请阅读观察者设计模式以获取更多信息。

我知道我喜欢说“要想更快得到帮助,请发布一个SSCCE”,但事情发展得太快了,我甚至都来不及回复! ;) - Andrew Thompson
//让我修改我的主类代码 public class Main { public static void main(String[] arg) { System.out.println(new TestSwing().createAndShowUI()); System.out.println("在用户输入文本之前,这行不应该被显示"); }} - bhuvan
我希望主类的代码能够等待用户输入。当我调用createAndShowUI()时,下面的代码应该在用户输入值并点击提交之前不执行。 - bhuvan
bhuvan:再次强调,你在评论中的代码尝试是行不通的。任何想要“等待”事件驱动GUI类的更改的类都需要“监听”该类 - 使用一种监听器或另一种监听器。请参阅我上面的编辑,其中使用PropertyChangeSupport变量并允许Swing类添加PropertyChangeListeners。再次强调,请阅读观察者模式 - Hovercraft Full Of Eels
非常感谢大家。我正在学习观察者设计模式,如果需要的话会再向你们请教。 - bhuvan

4
Swing是事件驱动的。无论您想在哪里使用msg,都应该作为操作监听器被调用的后果来调用它。
或者,您可以使用JOptionPane中的现成解决方案之一。
String userInput = JOptionPane.showInputDialog("Please enter ...");

我可以使用JOptionPane,但问题仍然是如何将文本字段的文本返回到主类中,我需要它在那里。 - bhuvan
@user87:阅读观察者设计模式,因为那是你所需要的。 - Hovercraft Full Of Eels

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