如何正确使用Actionlistener事件的输入

时间:2013-11-11 00:23:51

标签: java multithreading swing input

我已经学习java几个月了,我遇到了一个问题,就是我使用actionlistener的输入方式。会发生什么,我有一个方法,允许用户在文本框中输入内容并输入它。完成后,字符串将被写入我的类中的公共静态字段,其值将从输入方法中获取并返回值,并将字段设置回空字符串。它工作正常,但有时控制台会抛出无害的nullpointerexception。从我所做的所有研究中我发现这两个线程正在引起某种冲突,但我还没有真正弄清楚为什么会发生这种情况或如何解决它。

输入时有时会出现错误。

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at javax.swing.text.PlainView.updateMetrics(Unknown Source)
at javax.swing.text.PlainView.lineToRect(Unknown Source)
at javax.swing.text.PlainView.modelToView(Unknown Source)
at javax.swing.text.FieldView.modelToView(Unknown Source)
at javax.swing.plaf.basic.BasicTextUI$RootView.modelToView(Unknown Source)
at javax.swing.plaf.basic.BasicTextUI.modelToView(Unknown Source)
at javax.swing.text.DefaultCaret.repaintNewCaret(Unknown Source)
at javax.swing.text.DefaultCaret$1.run(Unknown Source)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$200(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

我的主要课程     包挂;

public class Main
{
  public static void main(String[] args)
  {
      GUIHandler gui = new GUIHandler();

      gui.handle();
      gui.person.tries = 6;

      while(true)
      {
          String t = gui.getInput("Put in input\n");
          System.out.println(t);

      }  
  }
}

我的GUIHandler类

public class GUIHandler implements ActionListener
{
public static String userInput = "";
public static boolean hasinputted = false;

public JFrame frame;
public Container pane;
public PersonComponent person;
public JLabel guessedChars;
public JLabel wordDisplay;

public JTextArea text;
public JScrollPane log;
public JTextField input;
public DefaultCaret bar;

public void handle()
{
    frame = new JFrame();

    frame.setTitle("Hangman");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setResizable(false);

    pane = frame.getContentPane();
    pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));

    person = new PersonComponent(); 
    person.tries = 0;
    person.setAlignmentX(Component.CENTER_ALIGNMENT);
    pane.add(person);

    guessedChars = new JLabel("placeholder");
    guessedChars.setFont(new Font(null, 0, 20));
    guessedChars.setAlignmentX(Component.CENTER_ALIGNMENT);
    pane.add(guessedChars);

    wordDisplay = new JLabel("placeholder");
    wordDisplay.setFont(new Font(null, 0, 20));
    wordDisplay.setAlignmentX(Component.CENTER_ALIGNMENT);
    pane.add(wordDisplay);

    text = new JTextArea(8, 40);
    text.setEditable(false);
    text.setFocusable(false);

    log = new JScrollPane(text);
    pane.add(log);

    bar = (DefaultCaret)text.getCaret();
    bar.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);

    input = new JTextField();
    input.setAlignmentX(Component.CENTER_ALIGNMENT);
    input.addActionListener(this);
    input.setEditable(false);
    pane.add(input);

    frame.pack();
    frame.setVisible(true);
}

@Override
public void actionPerformed(ActionEvent event)
{
    if(!input.getText().equals(""))
    {
        userInput = input.getText();
        input.setEditable(false);
        input.setText("");
        hasinputted = true;
    }
}

public String getInput(String message)
  {
    String temp = "";
    this.text.append(message);
    this.input.setEditable(true);

    while(true)
    {
        if(hasinputted)
        {
            temp = userInput + "\n";
            userInput = ""; 
            hasinputted = false;
            break;
        }
    }
    return temp;
  }
}

2 个答案:

答案 0 :(得分:2)

首先。启动Java应用程序时,会加载main方法,通常称为“主”线程。

Swing使用它自己的线程来监视事件并执行它的功能,这被称为事件调度线程。

Swing是一个单线程环境,这意味着预期所有对UI的修改和交互都将在EDT的上下文中完成。

你的整个例子都是侥幸。基本上在EDT上下文中运行任何类型的长时间运行任务或阻止操作(例如循环)将导致应用程序停止并停止响应用户交互和绘制请求。

首先查看Concurrency in Swing了解更多详情,并特别注意名为Initial Threads的部分

实现您似乎想要做的事情的常用方法是使用某种对话框。有关详细信息,请查看How to make dialogs。或者使用观察者模式,类似于ActionListener

基于评论的示例

基本上,此示例将“等待”,直到用户在文本字段中按 Enter 键时(更准确地通知)。它会将值附加到文本区域......

enter image description here

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class HangManTest {

    public static void main(String[] args) {
        new HangManTest();
    }

    public HangManTest() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JTextField field;
        private JTextArea area;

        public TestPane() {
            field = new JTextField(20);
            area = new JTextArea(10, 20);
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;

            add(field, gbc);
            add(new JScrollPane(area), gbc);

            field.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    String text = field.getText();
                    field.setText(null);
                    processGuess(text);
                }
            });

        }

        public void processGuess(String text) {
            area.append(text + "\n");
        }
    }

}

答案 1 :(得分:1)

您似乎太习惯命令行程序来真正理解事件驱动的GUI编程。这是一个小课。

GUI编程是事件驱动的。这意味着GUI程序应该等待用户做某事,然后做出回应。这对GUI编程非常重要,Java实际上为您处理事件的所有工作。您似乎正在尝试创建某种辅助事件循环,就像您在命令行程序中看到的那样,您应该让Java执行事件处理。

看起来你要做的就是将一个刽子手应用程序与从用户那里获取输入并将其打印到控制台的内容组合在一起。为此,只需在ActionListener中将消息打印到控制台即可。然后摆脱text文本区域。如果您认为需要在用户需要输入时附加文本“Put in input”的文本区域,则可以将文本区域的初始文本设置为“Put in input”然后将其附加到动作侦听器中。

完成上述更改后,您可以从getInput删除main方法和奇怪的循环。作为最后的改变,为了更好的衡量,您可能想要了解构造函数并将代码放在构造函数中创建窗口。

希望这有助于您了解事件驱动的GUI编程,并在Java中编写GUI,而不会有数千个大错误需要花费大量精力修复。