如何将来自 JTextField 的用户输入放入另一个方法

How to put user input from JTextField into another method

今天我写了一个 Hangman 的实现(下面的代码),它最初是为在命令行上使用而编写的。我正在让它在一些简单的 GUI 上工作,我几乎已经让它在 GUI 上工作,但是我很难确定如何将用户输入从 JTextField 获取到适当的部分代码(如在 play() 方法中调用的 userGuess() 方法)。

我知道链接到 JTextField 的动作侦听器将在 EDT 上 运行 & 所以这不是一个独立于 play() 线程的线程吗?我需要将信息从 EDT 线程发送到主线程吗?我知道我可以使用 .getText() 获取文本,但我希望代码等待用户进行猜测(即阻止),就像它在命令行中等待 Scanner(System.in) 被使用。

所以基本上,我想问的是如何从 JTextField 获取用户输入并将其传输到另一个 运行ning 方法?感谢您的帮助。

编辑: 好的,我已经将 GUI 代码与游戏代码分开了

刽子手游戏Class

    public class HangmanGame {
        public static void main(String[] args){
            HangmanModel hangmanModel = new HangmanModel();
            hangmanModel.readFile("words.txt");
            hangmanModel.play();
        }
    }

刽子手模型Class

    import java.io.*;
    import java.util.*;
    import java.util.List;


    public class HangmanModel {
        String[] wordList;
        Set<Character> alphabet;


        Scanner k = new Scanner(System.in);

        boolean keepGoing;
        boolean keepPlaying;    // allows the player to play another game of hangman
        int GuessesRemaining;
        HangmanView hangmanView = new HangmanView();

        // checkIfWon - sees if the user has won the game
        private boolean checkIfWon(){
            for(boolean tof : hangmanView.getLettersRevealed()){
                if(!tof)
                    return false;
            }
            return true;
        }

        // chooseSecretWord - selects a word
        private String chooseSecretWord(String[] wordList){
            return wordList[(int)(Math.random() * wordList.length)];
        }

        // createAlphabetSet - Creates the alphabet set that's used to ensure that the user's guess not a number nor a special character
        private void createAlphabetSet(){
            alphabet = new HashSet<Character>(26);
            for(Character c = 'a'; c<='z'; c++){
                alphabet.add(c);
            }
        }

        // play - Initiates a game of hangman
        public void play(){
            keepPlaying = true;
            createAlphabetSet();

            while(keepPlaying) {
                setUpGame();
                hangmanView.appendMessage("Welcome to hangman! You have 5 guesses to guess the secret word.\n");
                System.out.println("Welcome to hangman! You have 5 guesses to guess the secret word.");

                // user's guess
                String guess;
                while (GuessesRemaining > 0 && keepGoing) {
                    hangmanView.drawSecretWord();

                    // No letters have been guessed by the user at the beginning.
                    if(hangmanView.getLettersGuessed().size() != 0) {
                        hangmanView.drawLettersGuessed();
                    }

                    guess = userGuess("Guess a letter:");
                    updateSecretWord(guess);

                    hangmanView.appendMessage("\n");
                    System.out.println();

                    if (checkIfWon()) {
                        keepGoing = false;
                        hangmanView.appendMessage("Well done! You guessed " + hangmanView.getSecretWord() + " with " + GuessesRemaining + " guesses left!\n");
                        System.out.println("Well done! You guessed " + hangmanView.getSecretWord() + " with " + GuessesRemaining + " guesses left!");
                    }
                }

                // If player runs out of guesses
                if (GuessesRemaining == 0) {
                    hangmanView.appendMessage("Tough luck. The secret word was " + hangmanView.getSecretWord() + "\n");
                    System.out.println("Tough luck. The secret word was " + hangmanView.getSecretWord());
                }

                playAgain("Would you like to play another game of hangman? (type yes or no)");
            }
        }

        // playAgain - Allows the user to play another game of hangman
        private void playAgain(String message){
            hangmanView.appendMessage(message + "\n");
            System.out.println(message);
            String ans = k.next().toLowerCase();

            if(ans.equals("yes")) {
                hangmanView.appendMessage("Okay! Let's play another game of hangman!\n\n\n");
                System.out.println("Okay! Let's play another game of hangman!\n\n");
            }else if(ans.equals("no")){
                keepPlaying = false;
                hangmanView.appendMessage("Fair enough. Bye!\n");
                System.out.println("Fair enough. Bye!");
            }else{
                playAgain("Invalid input. Please type in yes or no: ");
            }
        }

        // readFile - read in wordList
        String[] readFile(String loc){
            try {
                File f = new File(loc);
                assert f.exists() : "File doesn't exist";

                BufferedReader input = new BufferedReader(new FileReader(f));

                // read in the stuff into an arrayList here
                wordList = input.readLine().split(" ");

                // close the input stream
                input.close();
            }catch(IOException ioException){
                ioException.printStackTrace();
            }
            return wordList;
        }

        // setUpGame - sets up the variables appropriately
        private void setUpGame(){
            keepGoing = true;
            GuessesRemaining = 5;
            hangmanView.setSecretWord(chooseSecretWord(wordList));
            hangmanView.setLettersRevealed(new boolean[hangmanView.getSecretWord().length()]);
            hangmanView.setLettersGuessed(new HashSet<Character>(26));     // 26 letters in alphabet
        }

        // updateSecretWord - updates which letters of the secret word have been revealed
        private void updateSecretWord(String l){
            List<Integer> changeBool = new ArrayList<Integer>();

            if(hangmanView.getSecretWord().contains(l)){
                // Searches through secretWord & notes down all letters that equal the user's guess
                for(int i=0; i<hangmanView.getSecretWord().length(); i++){
                    if(hangmanView.getSecretWord().charAt(i) == l.charAt(0))
                        changeBool.add(i);
                }
                hangmanView.newLetterRevealed(changeBool);

            }else{
                GuessesRemaining--;
                hangmanView.appendMessage("Letter not found. You have " + GuessesRemaining + " guesses remaining.\n");
                System.out.println("Letter not found. You have " + GuessesRemaining + " guesses remaining.");
            }
        }

        // get input from the user
        private String userGuess(String message){
            hangmanView.appendMessage(message + "\n");
            System.out.println(message);
            String l = k.next().toLowerCase();

            if(l.length() == 0) {
                l = userGuess("Nothing has been typed. Please guess a letter:");
            }else if(l.length() > 1) {
                l = userGuess("More than one letter was typed. Please select only one letter:");
            }else if(hangmanView.getLettersGuessed().contains(l.charAt(0))){
                l = userGuess("Letter already guessed. Please choose another letter:");
            }else if(!alphabet.contains(l.charAt(0))){
                l = userGuess("Not a letter. Please choose a letter:");
            }

            hangmanView.addLetterGuessed(l.charAt(0));
            return l;
        }
    }

HangmanView Class

    import javax.swing.*;
    import javax.swing.text.BadLocationException;
    import javax.swing.text.Document;
    import java.awt.*;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.util.*;
    import java.util.List;


    public class HangmanView {
        private String secretWord;
        private Set<Character> lettersGuessed;    // letters the user has guessed
        private boolean[] lettersRevealed;       // determines if the letter should be revealed or not

        // GUI
        private Document doc;
        private JTextField textField;

        public HangmanView(){
            buildGUI();
        }

        // addLetterGuessed - adds the new letter that was guessed by the user to lettersGuessed
        void addLetterGuessed(char letter){
            lettersGuessed.add(letter);
        }

        // appendMessage - appends text to the text area
        void appendMessage(final String message){
            SwingUtilities.invokeLater(
                    new Runnable(){
                        @Override
                        public void run(){
                            try{
                                doc.insertString(doc.getLength(), message, null);
                            }catch(BadLocationException badLocationException){
                                badLocationException.printStackTrace();
                            }
                        }
                    }
            );
        }

        // buildGUI - builds the GUI
        void buildGUI(){
            JFrame f = new JFrame("Hangman");

            // Text area
            JTextArea textArea = new JTextArea();
            doc = textArea.getDocument();
            textArea.setEditable(false);
            textArea.setLineWrap(true);
            textArea.setWrapStyleWord(true);

            JScrollPane jsp = new JScrollPane(textArea);

            // Text field for user to type letters in
            textField = new JTextField(10);      // TODO fix the textField's size
            JButton guessButton = new JButton("Guess");

            // Add listeners to textField & guessButton
            TextListener textListener = new TextListener();
            guessButton.addActionListener(textListener);
            textField.addActionListener(textListener);

            // Add everything to frame
            JPanel panel = new JPanel();
            panel.add(BorderLayout.WEST, textField);
            panel.add(BorderLayout.EAST, guessButton);
            f.add(BorderLayout.CENTER, jsp);
            f.add(BorderLayout.SOUTH, panel);

            f.setSize(300, 300);
            f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            f.setVisible(true);
        }

        // drawLettersGuessed - Outputs the letters guessed
        void drawLettersGuessed(){
            appendMessage("Letters already guessed: ");
            System.out.print("Letters already guessed: ");
            for (Character el : lettersGuessed) {
                appendMessage(el + " ");
                System.out.print(el + " ");
            }
            appendMessage("\n");
            System.out.println();
        }

        // drawSecretWord - draws the secret word with dashes & etc for user to use to guess the word with
        void drawSecretWord(){
            StringBuilder word = new StringBuilder();
            for(int i=0; i<lettersRevealed.length; i++){

                if(lettersRevealed[i]){
                    String s = secretWord.charAt(i) + " ";
                    word.append(s);
                }else{
                    word.append("_ ");
                }
            }
            appendMessage(word + "\n");
            System.out.println(word);
        }

        // newLetterRevealed - updates lettersRevealed with the new letter that has been guessed by the user
        void newLetterRevealed(List<Integer> change){
            // Changes the boolean value for those letters @ their corresponding indexes
            for(Integer idx : change)
                lettersRevealed[idx] = true;
        }


        // GETTERS
        String getText(){
            return textField.getText();
        }

        String getSecretWord(){
            return secretWord;
        }

        Set<Character> getLettersGuessed(){
            return lettersGuessed;
        }

        boolean[] getLettersRevealed(){
            return lettersRevealed;
        }

        // SETTERS
        void setLettersGuessed(HashSet<Character> s){
            lettersGuessed = s;
        }

        void setLettersRevealed(boolean[] a){
            lettersRevealed = a;
            Arrays.fill(lettersRevealed, false);
        }

        void setSecretWord(String word){
            secretWord = word;
        }

        void setText(final String t){
            SwingUtilities.invokeLater(
                    new Runnable(){
                        @Override
                        public void run(){
                            textField.setText(t);
                        }
                    }
            );
        }

        // ActionListener       -- TODO need the input from the the textField to go into userGuess method somehow
        private class TextListener implements ActionListener{
            @Override
            public void actionPerformed(ActionEvent ev){
                appendMessage(getText());
                setText(null);
            }
        }
    }

如果 GUI 和游戏 class 分开会更容易。游戏 class 处理游戏逻辑,而 GUI 管理用户界面,作为用户和游戏逻辑之间的桥梁。

因此,将文本字段事件发送到游戏就是监听文本字段的按键事件并将角色发送到游戏的 guess(char c) 方法 class.

每次调用guess()方法都可能触发猜错、游戏解决、运行猜错等事件。 GUI class 应该监听这些事件并向用户提供适当的反馈。

GUI 在 operation/event 驱动中是非线性的。有些事情发生了,你响应那个变化,不像命令行程序往往是非常线性的。

您需要为您感兴趣的事件设置回调并相应地响应这些事件,相应地更新 UI 和数据的状态...

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 java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class HangMan {

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

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

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

    public class TestPane extends JPanel {

        private List<JLabel> listSecrets;
        private String secretWord = "rabbit";
        private Set<Character> guesses;
        private JTextField guessField;

        private JLabel numberOfGuessesLabel;
        private int numberOfGuesses = 0;

        public TestPane() {
            guesses = new HashSet<>(25);
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;

            guessField = new JTextField(2);
            JButton applyGuess = new JButton("Apply");

            JPanel guessPane = new JPanel();
            guessPane.add(new JLabel("Make a guesss:"));
            guessPane.add(guessField);
            guessPane.add(applyGuess);

            add(guessPane, gbc);

            listSecrets = new ArrayList<>(25);

            JPanel guessesPanel = new JPanel();
            for (char c : secretWord.toCharArray()) {
                JLabel label = new JLabel("_");
                listSecrets.add(label);
                guessesPanel.add(label);
            }

            add(guessesPanel, gbc);

            numberOfGuessesLabel = new JLabel("0");
            add(numberOfGuessesLabel, gbc);

            GuessHandler handler = new GuessHandler();
            guessField.addActionListener(handler);
            applyGuess.addActionListener(handler);
        }

        public class GuessHandler implements ActionListener {

            @Override
            public void actionPerformed(ActionEvent e) {
                String text = guessField.getText();
                // This section should be part of your model layer, but for brevity
                // I've implemented directly....
                if (text.trim().length() > 0) {
                    if (!guesses.contains(text.charAt(0))) {
                        guesses.add(text.charAt(0));

                        int index = -1;
                        int searchFrom = 0;
                        while ((index = secretWord.indexOf(text, searchFrom)) != -1) {
                            JLabel label = listSecrets.get(index);
                            label.setText(Character.toString(text.charAt(0)).toUpperCase());
                            searchFrom = index + 1;
                        }

                        numberOfGuesses++;
                        numberOfGuessesLabel.setText(String.valueOf(numberOfGuesses));
                    }
                }
                guessField.setText(null);
                guessField.requestFocusInWindow();
            }

        }

    }

}

通常,我更愿意将游戏逻辑从 UI 中分离出来,允许 UI 对 data/model 进行修改并更新 UI为了响应这些变化,为简洁起见,我一直在 UI.

这个俗称Model-View-Controller. You can also see more details in Creating a GUI With JFC/Swing