今天我写了一个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);
}
}
}
答案 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类应该监听这些事件并向用户提供适当的反馈。