1

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

输入时有时会出现错误。

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

2 回答 2

2

首先。当一个 Java 应用程序启动时,main方法被加载在通常所说的“主”线程中。

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

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

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

首先查看Swing 中的并发以了解更多详细信息,并特别注意名为Initial Threads的部分

实现您似乎想要做的事情的常用方法是使用某种对话。查看如何制作对话框以获取更多详细信息。或者使用观察者模式,类似于ActionListener

基于评论的示例

基本上,这个例子将“等待”直到(更正确地被通知)用户按下Enter文本字段中的键。它会将值附加到文本区域...

在此处输入图像描述

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

}
于 2013-11-11T00:31:34.113 回答
1

您似乎太习惯于命令行程序而无法真正理解事件驱动的 GUI 编程。这是一个小教训。

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

看起来您正在尝试做的是将一个hangman 应用程序与从用户那里获取输入并将其打印到控制台的东西结合起来。为此,只需将消息打印到ActionListener. 然后摆脱text文本区域。如果您确实需要在用户需要输入时添加文本“Put in input”的文本区域,您可以将文本区域的初始文本设置为“Put in input”,然后将其附加到动作侦听器中。

进行上述更改后,您可以getInputmain. 作为最后的更改,为了更好地衡量,您可能想要了解构造函数并将代码以在构造函数中创建窗口。

希望这可以帮助您理解事件驱动的 GUI 编程,并在 Java 中编程 GUI,而不会出现数千个需要大量工作来修复的大错误。

于 2013-11-11T00:43:40.867 回答