Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
First, I figured I would try to write a standard hangman program without the GUI. I was able to succeed doing that. But I'm having trouble getting it into the GUI. I seem to struggle with GUIs the most.
I'll show my code so far. Maybe you guys could point me in the right direction? Or tell me if I'm on the right track?
Driver:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Hangman {
public static void main (String[] args){
JFrame frame = new JFrame ("Hangman");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
HangmanPanel hmp = new HangmanPanel();
frame.getContentPane().add(hmp);
frame.pack();
frame.setVisible(true);
}
}
Panel Class (this one is a bit messy, because I'm trying to experiment, and I'm going through a trial and error process I guess) My apologies if its logically off.
import java.awt.Panel;
import java.util.Scanner;
import java.util.Random;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class HangmanPanel extends JPanel {
private Hangman hm;
Scanner input = new Scanner(System.in);
final String[] WordList = { "ADA", "COBOL", "LOGO", "BASIC", "PROLOG",
"UBUNTU", "UHURU" };
String ChosenWord = WordList[(int) Math.random() * WordList.length];
StringBuilder display = new StringBuilder(ChosenWord.length());{
for (int i = 0; i < ChosenWord.length(); i++)
display.append("*");
int NumberOfTries = 0;
System.out.print("Let's Begin \n\n");
System.out.println("Try to guess the word in 6 tries, or you're a dead man!\n\n");
boolean correct = false;
while (NumberOfTries < 6 && !correct) {
String UserGuess = input.next();
String Letter = UserGuess.substring(0, 1);
if (ChosenWord.indexOf(Letter) < 0) {
System.out
.printf("The letter %s does not appear anywhere in the word.\n",
Letter);
NumberOfTries++;
}
else {
if (display.indexOf(Letter) >= 0)
System.out
.printf("The letter %s has already been entered as a guess.\n",
Letter);
else {
for (int p = 0; p < ChosenWord.length(); p++)
if (ChosenWord.charAt(p) == Letter.charAt(0))
display.setCharAt(p, Letter.charAt(0));
}
}
correct = display.indexOf("*") < 0;
draw(NumberOfTries);
}
if (correct)
System.out
.println("You have guessed "
+ ChosenWord
+ " correct and saved yourself from the gallos. Till next time that is.\n");
else {
System.out
.printf("You've had %d strikes against you. Thus you've been hung. Better luck next time.\n",
NumberOfTries);
}
}
public static void draw(int num) {
JPanel p1 = new JPanel();
p1.setBorder(BorderFactory.createEtchedBorder());
final String[] status = { "____\n| |\n | \n|\n|",
"____\n| |\n| O\n|\n|\n|", "____\n| |\n| O\n|/|\n|\n|",
"____\n| |\n| O\n|/|\\\n|\n|", "____\n| |\n| O\n|/|\\\n|/\n|",
"____\n| |\n| O\n|/|\\\n|/\\\n|" };
p1.add(new JLabel("Etched Border"));
if (num >= 0 && num < status.length) {
System.out.println(status[num]);
}
else {
System.out.println("Must be a Mistake. Out of Range.");
}
}
}
Swing (and most GUI's) are event driven environments. This basically means execution of logic is normally handled by event listeners/handlers
Start by becoming familiar with how to write general user interfaces, take a look at Creating a GUI with Swing
Using things like while-loops to get user input won't work within a GUI environment, you need to build the concept of what you want to do using the available controls
Related
I'm making a hangman game for school and I've run into a problem that I simply can't solve, maybe I'm overthinking, maybe I'm not. Anyways, I need to let the user input a letter, and if that letter is in the word used for the game (pikachu. I know, stupid choice but it's pretty basic and easy so I used that) then the letter is revealed, the problem is that after inputting a letter, the user can't guess any more letters. I need a way to loop through the letter input and revealing so that I can actually play the game.
I'm sorry if the solution is so simple but I just can't figure out what needs to change in my code in order to fix my problem because I'm very new to java.
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.*;
import java.awt.event.KeyListener;
import javax.swing.*;
public class PanDisp extends JPanel {
JLabel lblOutput;
JLabel lblGuess;
JButton btnUpdateLabel;
Image imgPkmn;
FraImg fraImg;
String sSecret;
public PanDisp() {//Constructor
KeyInput keyInput = new KeyInput();
KeyInput.LabelChangeListener labelChange = keyInput.new LabelChangeListener();
sSecret = "*******";
lblGuess = new JLabel("Type will go here");
lblOutput = new JLabel(sSecret);
btnUpdateLabel = new JButton("Enter");
add(lblOutput);
add(btnUpdateLabel);
addKeyListener(new KeyInput());
setFocusable(true);
btnUpdateLabel.addActionListener(labelChange);
fraImg = new FraImg(imgPkmn);
}
public void GameOver() {
}
class KeyInput implements KeyListener {
String sInput;
String sWord = "pikachu";
String sSecret = "*******";
char chInput;
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
chInput = (char) e.getKeyChar();
sInput = String.valueOf(chInput);
lblOutput.setText(sInput);
}
#Override
public void keyReleased(KeyEvent e) {
}
class LabelChangeListener implements ActionListener {
char cWord;
int nCorrect, nIncorrect, nNum;
public void actionPerformed(ActionEvent event) {
if (sWord.contains(sInput)) {
for (int i = 0; i < sWord.length(); i++) {
sSecret.replace(sSecret.charAt(i), sWord.charAt(i));
}
nCorrect += 1;
}
else {
nIncorrect += 1;
if (nIncorrect == 7) {
GameOver();
}
}
}
}
}
}
Your problem is that your mindset is off and has to be changed. Don't think "loop", and in fact get "loop" out of the equation. You're programming in an event-driven programming environment, and the loop you're thinking of belongs in the linear console programming environment. Instead think "state of object" and "behavioral changes to state changes", and you'll move much further in this quest. So change the state of your class -- number of guesses, number of correct guesses, and then change the response to the user's input based on this state
For instance, if you wanted to create a console program that allowed a user to enter 5 Strings, and then displayed those Strings back to the user, it would be pretty straight forward, in that you'd create your String array, and then use a for loop to prompt the user 5 times to enter text, grabbing each entered String within the loop. Here "loops" like the one you're requesting work.
Linear Console Program
import java.util.Scanner;
public class Enter5Numbers1 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("Please enter 5 sentences:");
String[] sentences = new String[5];
for (int i = 0; i < sentences.length; i++) {
System.out.printf("Enter sentence #%d: ", (i + 1));
sentences[i] = scanner.nextLine();
}
System.out.println("You entered the following sentences:");
for (String sentence : sentences) {
System.out.println(sentence);
}
scanner.close();
}
}
If on the other hand you wanted to create a GUI that did something similar, that prompted the user for 5 Strings and accepted those Strings into an array, you couldn't use the same type of for loop. Instead you would need to give your class an int String counter, perhaps called enteredSentenceCount, and in a JButton's ActionListener (or Action -- which is something very similar), you would accept an entered String (perhaps typed into a JTextField called entryField), only if the enteredSentenceCount is less than 5, less than the maximum number of Strings allowed. You would of course increment the enteredSentenceCount variable each time a String is entered. And this combination of increase a counter variable and checking its value will need to substutite for the concept of a "loop". So here the "state" of the class is held in the enteredSentenceCount, and the behavioral change we want is to alter what the button's Action does depending on the enteredSentenceCount's value -- if less than 5, accept a String, and if it is equal to or greater than 5, display the entered Strings.
Event Driven GUI Program
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class Enter5Numbers2 extends JPanel {
private static final int MAX_SENTENCE_COUNT = 5; // number of Strings to enter
private static final String PROMPT_TEMPLATE = "Please enter sentence number %d:";
private String[] sentences = new String[MAX_SENTENCE_COUNT]; // array to hold entered Strings
private int enteredSentenceCount = 0; // count of number of Strings entered
private JTextField entryField = new JTextField(20); // field to accept text input frm user.
// JLabel to display prompts to user:
private JLabel promptLabel = new JLabel(String.format(PROMPT_TEMPLATE, (enteredSentenceCount + 1)));
public Enter5Numbers2() {
// create GUI
// First create Action / ActionListener for button
EntryAction entryAction = new EntryAction("Enter");
JButton entryButton = new JButton(entryAction); // pass it into the button
entryField.setAction(entryAction); // but give it also to JTextField so that the enter key will trigger it
// JPanel to accept user data entry
JPanel entryPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
entryPanel.add(entryField);
entryPanel.add(entryButton);
// allow main JPanel to display prompt
setBorder(BorderFactory.createTitledBorder("Please Enter 5 Sentences"));
setLayout(new GridLayout(2, 1));
add(promptLabel);
add(entryPanel);
}
// Action class, similar to an ActionListener
private class EntryAction extends AbstractAction {
public EntryAction(String name) {
super(name);
putValue(MNEMONIC_KEY, (int) name.charAt(0));
}
#Override
public void actionPerformed(ActionEvent e) {
// check that we haven't entered more than the max number of sentences
if (enteredSentenceCount < MAX_SENTENCE_COUNT) {
// if OK, get the entered text
String sentence = entryField.getText();
// put it in our array
sentences[enteredSentenceCount] = sentence;
entryField.setText(""); // clear the text field
entryField.requestFocusInWindow(); // set the cursor back into the textfield
enteredSentenceCount++; // increment our entered sentence count variable
promptLabel.setText(String.format(PROMPT_TEMPLATE, (enteredSentenceCount + 1))); // change prompt
}
// if the number of sentences added equals the number we want, display it
if (enteredSentenceCount == MAX_SENTENCE_COUNT) {
JTextArea textArea = new JTextArea(6, 30);
for (String sentence : sentences) {
textArea.append(sentence + "\n");
}
JScrollPane scrollPane = new JScrollPane(textArea);
JOptionPane.showMessageDialog(Enter5Numbers2.this, scrollPane, "Five Sentences Entered",
JOptionPane.PLAIN_MESSAGE);
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Enter 5 Numbers");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(new Enter5Numbers2());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
Ok, I am sorry I am repeating questions that have already been asked but I have searched and searched and searched and nobody's answers seemed to have helped me... I tried the following questions:
JButton "stay pressed" after click in Java Applet
JButton stays pressed when focus stolen by JOptionPane
(I apologize if I'm just being dumb.. it is hard to relate to my code)
I have tried everything: using another thread to handle all the stuff, changing the JFrame to a JDialog as apparently they are "modal" so it would work independently. But that didn't seem to work either. I am stuck now so I am using my last resource (asking Stack Overflow).
What I am trying to do is get the user to enter some numbers in a textfield (4,2,7) then they press a JButton "Calculate Mean" and it finds the mean of the numbers and displays it in a JOptionPane message. When the user closes the JOptionPane dialog box they should be able to edit the numbers and do it again but the "Calculate Mean" button stays pressed and the user can't do anything but close the window. Even pressing the Tab key doesn't change anything. Does anyone know why this is? My code is down below:
PLEASE FORGIVE ME IF MY CODE IS HARD TO READ! I spent a very long time trying to indent it all correctly and I also tried to make it as short as possible by taking out any bits unrelated to the question. I was unsure which bits to take out so there still might be some unnecessary bits...
I am sorry for my messy code but this is the code:
package MathsProgram_II;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
public class Mean implements Runnable {
JFrame meanFrame = new JFrame(); //I tried changing this to dialog
JPanel meanPanel = new JPanel(new GridBagLayout());
JLabel enterNums = new JLabel("Enter Numbers: ");
JTextField txtNums = new JTextField(20);
JButton calculate = new JButton("Calculate Mean");
boolean valid = true;
double answer = 0;
ButtonListener bl = new ButtonListener();
public synchronized double[] getArray() {
String nums = txtNums.getText();
String[] numsArray = nums.split(",");
double[] doubleArray = new double[numsArray.length];
if (nums.isEmpty() == true) {
JOptionPane.showMessageDialog(meanFrame, "You did not enter anything!",
"Fail", JOptionPane.ERROR_MESSAGE);
valid = false;
calculate.setEnabled(false);
} else {
for (int i = 0; i < numsArray.length; i++) {
try {
doubleArray[i] = Double.parseDouble(numsArray[i]);
} catch (NumberFormatException nfe) {
JOptionPane.showMessageDialog(meanFrame, "Error getting numbers!",
"Error", JOptionPane.ERROR_MESSAGE);
valid = false;
}
}
}
return doubleArray;
}
public synchronized void calculateMean() {
ArrayList<Double> numbersList = new ArrayList<Double>(20);
double[] theNumbers = getArray();
double tempAnswer = 0;
if (valid == true) {
int length = theNumbers.length;
for (int i = 0; i < theNumbers.length; i++) {
numbersList.add(theNumbers[i]);
}
for (int i = 0; i < length; i++) {
double y = numbersList.get(i);
tempAnswer = tempAnswer + y;
}
this.answer = tempAnswer / length;
//I ALSO TRIED DOING THIS:
txtNums.requestFocus();
calculate.setEnabled(false);
showMean();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
}
}
public void showMean() {
JOptionPane.showMessageDialog(meanFrame, "The Mean: " + answer, "The Mean of Your Numbers", JOptionPane.INFORMATION_MESSAGE);
}
private class ButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == calculate) {
meanFrame.remove(meanPanel);
meanFrame.setVisible(true);
calculateMean();
}
}
}
}
Don't remove the panel from your mainframe.
Don't use "synchronized" on your "calculateMean()" method. The code is executed on the Event Dispatch Thread (EDT) so it will be single threaded.
Don't use a Thread.sleep() on the EDT. This will prevent the GUI from repainting itself.
I have tried a bunch of arrangements for my code, and this is my last version of code. I am trying to create a window, that will cycle through my 5 questions, and the end it will display how many you got correct.
Currently after entering my first answer in the text field it jumps to the end of the array. After that it will continually stay on the same question.
Sorry if I have made many coding errors, as this is my first time creating a program after watching a bunch of tutorials. Thanks for any help!
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class testwin extends JFrame {
private JTextField input;
private JLabel problem;
private String[] questions = new String[5];
private String[] answers = new String[5];
private String[] response = new String[5];
private int total = 5;
private int result = 0;
private String mark;
public testwin(){
super("Pop Quiz");
setLayout(new FlowLayout());
questions[0] = "Solve for x \t (x + 3)*2-10 = 4";
questions[1] = "Factorize \t x^2 + 10x + 21";
questions[2] = "Find the square root of 64";
questions[3] = "Multiply 23 and 94";
questions[4] = "Add 2145, 1452, 253,1414";
answers[0] = "4";
answers[1] = "(x + 3)(x + 7)";
answers[2] = "8";
answers[3] = "2162";
answers[4] = "5264";
problem = new JLabel(questions[0]);
add(problem);
input = new JTextField("Answer goes here",20);
add(input);
mark = String.format("You got %s correct out of %s", result,total);
input.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent event){
int y = 0;
int x = 0;
if(event.getSource() == input){
while(x < questions.length){
problem.setText(questions[x]);
response[y] = String.format("%s",event.getActionCommand());
input.setText("Answer goes here");
x++;
y++;
}
}
if(x == 4)
JOptionPane.showMessageDialog(null, mark);
for(int z = 0; z < questions.length; z++){
if(response[z] == answers[z])
result++;
}
}
}
);
add(input);
setSize(250,250);
setVisible(true);
}
}
During each actionPerformed call you cycle through the whole list. Here:
while(x < questions.length){
problem.setText(questions[x]);
response[y] = String.format("%s",event.getActionCommand());
input.setText("Answer goes here");
x++;
y++;
}
So by the time the text is actually displayed again, you have set to it to each different question, but stop with the last one and that is what the user actually sees. You only want to change the text once, to the next question, everytime an action is performed.
You'll need to keep some sort of counter for which question you are on that can be accessed by the actionPerformed method
Also, as mentioned in the comments, you will want to change your result checking to use the equals method. Strings can't be compared using the == sign because for Strings == compares the reference each String object is pointing to, not the value of the String object
if(response[z].equals(answers[z]))
result++;
The issue is the while() loop inside your ActionListener, it will always iterate through the entire set of questions and finish at the last entry. This is due to the fact that your x and y variables are local to the scope of the ActionListener, and as such, are always reset to 0 and looped on each input click.
To fix this, you don't need a while loop, just make your x variable a private class field (questionIndex), use an if statement ensure the index is within the array bounds, and update the problem and response values accordingly.
Here's some psuedocode that should do the right thing:
private int questionIndex = 0;
public void actionPerformed(ActionEvent event){
if(event.getSource() == input){
if(questionIndex < questions.length){
problem.setText(questions[questionIndex]);
response[questionIndex] = String.format("%s",event.getActionCommand());
input.setText("Answer goes here");
questionIndex++;
}
...
}
}
Given my code below how can I make the paint applet open up when I run the main code? I thought extends would do the trick but nothing has come up. I can't get the program to execute one body part at a time. I don't have enough time, but at the very least I would like it to show the hangman drawing as soon as the main is executed.
import java.awt.Graphics;
import java.util.Scanner;
import javax.swing.JApplet;
public class HangmanLogic extends HangmanGuy {
public static void main(String[] args) {
int count = 0;
Scanner in = new Scanner(System.in);
System.out.println("Enter a 4 or 5 letter word and the computer will play hangman against you!");
String word = in.nextLine();
char[] letter = word.toCharArray();
for (int i = 0; i < letter.length; i++) {
letter[i] = 'a';
}
for (int i = 0; i < word.length(); i++){
for (int j = 48; j < 122; j++) {
count++;
if (letter[i] == word.charAt(i)) {
break;
} else {
letter[i] = (char)((int) j + 1);
}
}
}
System.out.println("Attempt to solve: " + count);
System.out.println("Your word is: ");
for (char letters : letter) {
System.out.print(letters);
}
}
}
import java.awt.Graphics;
import javax.swing.JApplet;
import java.awt.*;
public class HangmanGuy extends JApplet
{
public void paint (Graphics Page)
{
//gallows
Page.drawLine(0,300,20,300);
Page.drawLine(10,40,10,300);
Page.drawLine(10,40,80,40);
Page.drawLine(80,40,80,55);
//torso
Page.drawOval(50,55,50,55);
Page.drawOval(50,100,50,100);
//left arm and hand
Page.drawLine(50,150,40,110);
Page.drawLine(40,110, 45,100);
Page.drawLine(40,110, 25,100);
Page.drawLine(40,110, 25,115);
//right arm and hand
Page.drawLine(100,150,120,110);
Page.drawLine(120,110, 115,95);
Page.drawLine(120,110, 125,95);
Page.drawLine(120,110, 135,115);
//left leg and foot
Page.drawLine(80,200,100,250);
Page.drawLine(100,250, 115,260);
//right leg and foot
Page.drawLine(75,200,60,250);
Page.drawLine(60,250,45,260);
}
}
Two things.
Why should it. Nothing is controlling it. Applets are suppose to be loaded and controlled by browsers;
Why are you mixing GUI and console paradigms? User input from GUI's is suppose to come from UI components and controls, not the command line.
Start by taking a look at Creating a GUI With JFC/Swing.
I'd recommend moving your UI to a JFrame instead, until you understand the basics of how to build a UI as applets bring there own issues which can stump you if you don't already have some background in how the UI works.
In fact. Start with a JPanel and when you're ready, add it to an instance of JFrame. When that works, you can try adding the panel to a JApplet
I have a JAVA card game, which show four cards at each round.
Currently, the stop between each round is waiting for a \n input in console. But I'd like to change it to waiting for a keyboard "enter" on the GUI.
Following is my current code. Please let me know how should I change it?
Millions of thanks!!!
import java.awt.GridLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
public class Game {
public static void main(String args[]) throws IOException {
Deck deck = new Deck();
deck.shuffle();
int aGame = 4;
List<Card> cards = new ArrayList<Card> ();
for(int i = 0; i < 52; i++) {
cards.add(deck.deck.get(i));
if(aGame == 1) {
System.out.println(deck.deck.get(i).toString());
System.out.println("Start!!!");
JFrame f = new JFrame("Calculating 24!");
GridLayout grid = new GridLayout(2, 2);
f.setLayout(grid);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
for(Card c: cards){
f.add(new LoadImageApp(c));
}
f.pack();
f.setVisible(true);
cards.clear();
while (true) {
char c = (char) System.in.read();
if (c == '\n') {
f.setVisible(false);
break;
}
}
aGame = 4;
if(i == 51) {
deck.shuffle();
i = -1;
}
}
else if(aGame == 4) {
System.out.println("Calculate based on the following four cards!");
System.out.println(deck.deck.get(i).toString());
aGame --;
}
else {
System.out.println(deck.deck.get(i).toString());
aGame --;
}
}
}
}
If the GUI element accepting the values is a JTextField, it is possible to add an ActionListener that will typically respond when the user hits Enter
Other tips
1)
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
Change that to:
f.setDefaultCloseOperaiton(JFrame.EXIT_ON_CLOSE);
Or better..
f.setDefaultCloseOperaiton(JFrame.DISPOSE_ON_CLOSE);
The first will have the same effect as what was, but the second will also check there are no non-daemon threads running prior to exit.
2)
Do not try to mix GUIs and the command line together. The way you go about writing an application for either is significantly different.
For Swing based applications key events should be handled using Key Bindings. However it may not be apparent to the end user that ENTER is required. A more UI centric approach is to use a dialog for this purpose
JOptionPane.showMessageDialog(frame, "Press ENTER to continue");
Using console based read methods such as InputStream#read is another source of confusion for users. Use a JTextComponent such as a JTextField to read user input in Swing applications.