如何将用户输入从JTextField放入另一个方法

时间:2015-02-17 01:46:52

标签: java swing user-interface text

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

我知道链接到JTextField的动作监听器将在EDT&那不是说play()的线程中有一个单独的线程吗?我是否需要将信息从EDT线程发送到主线程?我知道我可以使用.getText()来获取文本,但是我想让代码等待用户猜测(即阻塞),就像它在Scanner(System.in)中在命令行中等待一样使用。

基本上,我问我如何从JTextField&获取用户输入?将它传输到另一个正在运行的方法?谢谢你的帮助。

编辑:好的,所以我将GUI的代码与游戏代码分开

HangmanGame Class

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

HangmanModel类

    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类

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

2 个答案:

答案 0 :(得分:2)

GUI在操作/事件驱动中是非线性的。有些事情会发生,你会对这种变化作出反应,而不像命令行程序那样往往非常线性。

您需要设置对您感兴趣的事件的回调并相应地响应这些事件,相应地更新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中维护。

这通常称为Model-View-Controller。您还可以在Creating a GUI With JFC/Swing

中查看更多详细信息

答案 1 :(得分:1)

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

因此,将文本字段事件发送到游戏是为了收听文本字段的按键事件,并将字符发送到游戏类的guess(char c)方法。

guess()方法的每次调用都可能触发错误猜测,游戏解决和猜测耗尽等事件。 GUI类应该监听这些事件并向用户提供适当的反馈。