【发布时间】:2015-02-17 01:46:51
【问题描述】:
今天我写了一个 Hangman 的实现(下面的代码),它最初是为了在命令行上使用而编写的。我正在让它在一些简单的 GUI 上工作,我几乎让它在 GUI 上工作,但是我很难确定如何将用户输入从 JTextField 获取到适当的部分代码(就像在 play() 方法中调用的 userGuess() 方法)。
我知道链接到 JTextField 的动作侦听器将在 EDT 上运行,所以不是与 play() 的线程分开的线程吗?我需要将信息从 EDT 线程发送到主线程吗?我知道我可以使用 .getText() 获取文本,但我希望代码等待用户进行猜测(即阻止),就像它在 Scanner(System.in) 时在命令行中的等待方式一样被使用了。
所以基本上,我问的是如何从 JTextField 获取用户输入并将其传输到另一个正在运行的方法?感谢您的帮助。
编辑:好的,我已经将 GUI 代码与游戏代码分开了
HangmanGame 类
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);
}
}
}
【问题讨论】:
-
以这种方式设计程序很棘手。你可能想用更无聊的方式来代替 - not 有一个
play方法,并让它让你的所有代码都在你的事件处理程序中。 -
@CPUTerminator 为什么?
ActionListener会做到这一点并且做得更好,考虑到 [Enter] 键可能不是“操作”键的平台差异...... -
GUI 是事件驱动的环境,发生了什么事,你就响应它。在响应事件时,您对模型和 UI 的状态进行更新......这不像命令行风格的程序更线性驱动......
-
为我之前的评论道歉,有一段时间没有接触 GUI,忘记了那些 ActionListener。要点是采用一种划分事件驱动的方法,其中代码仅在特定事件发生时执行(与纯粹的过程相反,从上到下流动)。
-
是的,在阅读您的代码时,我同意 MadProgrammer 和 @immibis:您想重新设计您的程序,使其成为事件驱动的,而不是线性的。
标签: java swing user-interface text