I am creating a hangman game and I was having trouble getting the jlabel that contained each character of the word to update after the right letter button has been clicked. I have been having trouble with this as I am relatively new to working with Java Guis. Below is the action listener for the letter buttons.
private class LetterHandler implements ActionListener{
private char letterVal;
public LetterHandler(char lv){
letterVal = lv;
}
//checks if clicked button has the correct value as word char
public void actionPerformed(ActionEvent e){
for(int x = 1; x <= selectedWord.wordLength; x++){
if(selectedWord.wordValue.charAt(x - 1) == letterVal){
// JLabel letterValLabel = new JLabel(String.valueOf(letterVal));
wordSpacesArray.get(x-1).setName(String.valueOf(letterVal));
wordSpacesArray.get(x-1).revalidate();
continue;
}
else{
continue;
}
}
checkWin();
triesLeft--;
triesLeftLabel.revalidate();
}
//finds if all jlabels are complete or not
public void checkWin(){
for(int x = 1; x <= wordSpacesArray.size(); x++){
String charVal;
charVal = String.valueOf(wordSpacesArray.get(x-1));
if(charVal != "?"){
System.out.println("youWon");
System.exit(0);
}
else{
break;
}
charVal = null;
}
}
}
Any help is appreciated. Let me know if you need for of the programs code Thanks :)
There are some issues with the code. However, I'll first try to focus on your current problem:
I assume that wordSpacesArray is a list that contains the JLabels of individual letters of the word.
When this ActionListener will be notified, you try to update the labels in wordSpacesArray with the letter that corresponds to this button. However, in order to update the text that is shown on a JLabel, you have to call JLabel#setText(String) and not JLabel#setName(String). So the line should be
wordSpacesArray.get(x-1).setText(String.valueOf(letterVal));
// ^ Use setText here!
Now, concerning the other issues:
As pointed out in the comments, you should use 0-based indexing
The calls to revalidate are not necessary
The use of continue in its current for is not necessary
You should not compare strings with ==, but with equals
// if(charVal != "?") { ... } // Don't do this!
if(!charVal.equals("?")){ ... } // Use this instead
But the charVal in this case will be wrong anyhow: It will be the string representation of the label, and not its contents. So you should instead obtain the text from the label like this:
// String charVal = String.valueOf(wordSpacesArray.get(x-1)); // NO!
String charVal = wordSpacesArray.get(x-1).getText(); // Yes!
The triesLeftLabel will not be updated as long as you don't call setText on it
I think the logic of the checkWin method is flawed. You print "You won" when you find the first letter that is not a question mark. I think it should print "You won" when no letter is a question mark.
You should not call System.exit(0). That's a bad practice. Let your application end normally. (In this case, maybe by just disposing the main frame, although that would also be questionable for a game...)
So in summary, the class could probably look like this:
private class LetterHandler implements ActionListener
{
private char letterVal;
public LetterHandler(char lv)
{
letterVal = lv;
}
// checks if clicked button has the correct value as word char
#Override
public void actionPerformed(ActionEvent e)
{
for (int x = 0; x < selectedWord.wordLength; x++)
{
if (selectedWord.wordValue.charAt(x) == letterVal)
{
wordSpacesArray.get(x).setText(String.valueOf(letterVal));
}
}
checkWin();
triesLeft--;
triesLeftLabel.setText(String.valueOf(triesLeft));
}
// finds if all jlabels are complete or not
public void checkWin()
{
for (int x = 0; x < wordSpacesArray.size(); x++)
{
String charVal = wordSpacesArray.get(x).getText();
if (charVal.equals("?"))
{
// There is still an incomplete label
return;
}
}
// When it reaches this line, no incomplete label was found
System.out.println("You won");
}
}
Related
So basically i need to be able to click a button, and each time its clicked it takes away 10 from 100. so after 10 clicked it should display 0.
public class numberclass{
public String numbermethod() {
int number = 100;
String result = Integer.toString(number);
return result;
}
}
//part of swing class below
String numberString = numberclass.numbermethod();
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
textArea.append(numberString);
}
}
});
obviously as it stands it only displays 100 in the text field upon a click. I have tried many ways to make this work, but i feel like i'm missing something crucial. i have tried to google this basic, basic problem, however i seem to not be able to word it correctly.
Start by subtracting something from something. Now, there's any number of ways you might achieve this, but lets start by using a method and maintain the class encapsulation...
public class NumberClass {
private int number = 100;
public void update() {
number -= 10;
}
public String numbermethod() {
String result = Integer.toString(number);
return result;
}
}
Then update the text to be displayed
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
numberClass.update();
String numberString = numberClass.numbermethod();
textArea.append(numberString);
}
});
You numbermethod() is flawed. You set number to 100 and return that value (100) as a String. It will never be something other than "100"! Move int number = 100; outside the method (member variable)
In your button listener add:
number -= 10; // or number = number - 10;
I created a dialog box and have the user enter 5 colors in it from memory. That all completely works, there's just a slight aesthetic problem. Upon entering all 5 colors correctly, or getting one incorrect, it's suppose to wipe the contents within the dialog box and print a message "Sorry! Incorrect color" or "Congratulations". It prints the message, but the JTextField can still be seen somewhat behind the message (A left over portion/clipping).
I've tried using the hide() and remove() methods but they didn't seem to work (Or I'm using them incorrectly), I tried re-making a dialog box upon either but I couldn't seem to solve the issue still. What am I doing wrong/how can I make the JTextField disappear upon completion? Thank you in advance for any help!
Here's the portion where if the user enters a color incorrectly or gets them all correct (txtName is the JTextField):
if(count == 6)//User either finished or entered a color incorrectly
{
//Entered color incorrectly
if(incorrect == true)
{
txtName.setEnabled(false); //Doesn't work
homeScreen.remove(txtName); //Doesn't work
labelName.setText("Incorrect! Sorry - Wrong color.");
//txtName.removeActionListener(new MyButtonListener());
}
else//Correctly finished the game.
{
labelName.setText("Congratulations - your memory skills are perfect!");
//txtName.removeActionListener(new MyButtonListener());
homeScreen.remove(txtName);//Doesn't work
}
}
Here's my entire program (I can't get it format properly in the post):
package memorygame;
import java.util.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.FlowLayout;
public class MemoryGame
{
private JFrame homeScreen;
private JLabel labelName;
private JTextField txtName;
private JLabel correct;
Vector<String> name = new Vector();
private int count = 1;
private MyButtonListener listen1 = new MyButtonListener();
//Constructor - Method to be called when MemoryGame object called
public void MemoryGame ()
{
homeScreen = new JFrame();
homeScreen.setSize(400,200);
homeScreen.setTitle("Memory Game");
homeScreen.setDefaultCloseOperation(homeScreen.EXIT_ON_CLOSE);
homeScreen.setLayout(new FlowLayout());
labelName = new JLabel();
txtName = new JTextField(10);
createContents();
homeScreen.setVisible(true);
}//End Constructor
//Create components and add them to the window/dialog box
private void createContents()
{
labelName.setText("Enter the color " + count + ":");
System.out.println("The current count is: " + count);
homeScreen.add(labelName);
homeScreen.add(txtName);
txtName.addActionListener(new MyButtonListener());//Allows you to press enter to invoke action
}
//Upon user hitting enter
private class MyButtonListener implements ActionListener
{
public void actionPerformed (ActionEvent e)//When event occurs
{
Scanner in = new Scanner (System.in);//For program input
String answer = "";
//Make memColor an array for randomized colors
/*
Random r = new Random();
String[] memColors = new String[5];
String[] colors = {"red", "green", "blue", "yellow", "brown", "purple"};
for(int i =0; i < memColors.length; i++)
{
memColors[i] = colors[r.nextInt(6)];
}
*/
String memColor1 = "red";
String memColor2 = "black";
String memColor3 = "yellow";
String memColor4 = "green";
String memColor5 = "blue";
boolean incorrect = false;
//If answered incorrectly set count to 5(it'll be 6)
//And have a boolean for if count== 6 for congrats and failure
if(e.getSource() == txtName)
{
answer = txtName.getText();
System.out.println(answer);
}
else
{}
//Check if user entered Correct color, 1= Red, 2= Black, etc.
if(count == 1)
{
if(answer.equalsIgnoreCase(memColor1))
{
txtName.setText("");
}
else
{//Needs to be a custom message box
count = 5;
incorrect = true;
}
}
else if(count == 2)
{
if(answer.equalsIgnoreCase(memColor2))
{
txtName.setText("");
}
else
{
count = 5;
incorrect = true;
}
}
else if(count == 3)
{
if(answer.equalsIgnoreCase(memColor3))
{
txtName.setText("");
}
else
{
count = 5;
incorrect = true;
}
}
else if(count == 4)
{
if(answer.equalsIgnoreCase(memColor4))
{
txtName.setText("");
}
else
{
count = 5;
incorrect = true;
}
}
else if(count == 5)
{
if(answer.equalsIgnoreCase(memColor5))
{
txtName.setText("");
}
else
{
count = 5;
incorrect = true;
}
}
else
{
JOptionPane.showMessageDialog(null, "Something went wrong!");
}
count += 1;
//User has completed the game or entered a color incorrectly
if(count == 6)
{
if(incorrect == true) //Incorrect color
{
txtName.setEnabled(false);
homeScreen.remove(txtName);
labelName.setText("Incorrect! Sorry - Wrong color.");
//txtName.removeActionListener(new MyButtonListener());
}
else //Completed the game correctly
{
labelName.setText("Congratulations - your memory skills are perfect!");
//txtName.removeActionListener(new MyButtonListener());
homeScreen.remove(txtName);
}
}
else
{
labelName.setText("Enter the color " + count + ":");
}
}//End Listener
}//End Button class
public static void main(String[] args) {
//Show message box
//Randomize colors
JOptionPane.showMessageDialog(null, "How good is your memory?\nTry to memorize this color sequence:\n\n red black yellow green blue");
MemoryGame mem = new MemoryGame();
mem.MemoryGame();
}//End Main
}// End Class
Use txtName.setVisible(false); instead of homeScreen.remove(txtName);
Basically, if you want to call remove, you will need to revalidate and repaint container...
You'll also want to ensure that your UI is create within the context of the Event Dispatching Thread, see Initial Threads for more details
Change the code
homeScreen.remove(txtName);
to
homeScreen.remove(txtName);
homeScreen.revalidate();
homeScreen.repaint();
The reason why remove() does not imply revalidate() + repaint() is that remove() is not atomic. The caller might want to perform multiple updates, a sequence of several add() and remove() calls. revalidate() basically "completes" your "UI update transaction", repaint() "pushes it to the screen".
As a side note, your code will be easier to understand and maintain, if you perform a small tiny improvements on variable names. What's homeScreen? And why is it called labelName - what name? And what's txtName - the name of what text? count of what, icecreams?
I suggest the following improvements:
incorrect -> isIncorrect (also change if (incorrect == true) to if (isIncorrect)
homeScreen -> mainFrame or just frame (as you only have one frame)
labelName -> infoLabel or just label (as you only have one label - and remove JLabel correct, it's unused)
txtName -> answerTextField
count -> answerCount
Remove variable listen1, it's not used.
Plus, if you look at the code that does if (count == 1) and the following four if clauses, they are all identical except for the number. A perfect situation for an array. You can convert the variables memColor* to an array String[] memColor. Or maybe that's what the Vector was for. You might instead want to use ArrayList, nobody uses Vector these days in such situations.
First of all, thank you for taking your time to read this.
this is what am trying to make:
A Text editor with multiple options.
a button for a virtual keyboard. I've managed to create the buttons, also successfully added all the buttons, however I am having difficulties linking each button to my text area and making each button press work.
Not looking for anything complex also all other aspects of the app works, as you will see from the screenshot provided.
this is the bits of code in relation to my keyboard.
class KbListener implements ActionListener //kb function.
{
public void actionPerformed(ActionEvent e) //checking events.
{
keyboard = new JFrame("VK");
keyboard.setSize(400,300);//setting initial size of app.
keyboard.setVisible(true);//making sure its active.
keyboard.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);//closes when the x is pressed.
JButton[] letter = new JButton[27];
keyboard.setLayout(new GridLayout(3,9));
for (int i = 0;i<27;i++)
{
letter[i] = new JButton(""+(char)('A'+ i));
keyboard.add(letter[i]);
//up until this point all is fine.
letter[i].addActionListener = (new ActionListener());
if(e.getSource() ==letter[A])
textArea.append("A");
}
}
}
You need to create a String that is used in the button and used in its listener both, something like,
for (int i = 0; i < 27; i++) {
final String buttonText = String.valueOf((char) ('A' + i));
letter[i] = new JButton(buttonText);
keyboard.add(letter[i]);
letter[i].addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
textArea.append(buttonText);
}
});
}
Note that buttonText must be final so that it is accessible within the anonymous inner ActionListener class.
Also, consider avoiding magic numbers. For instance, you could do
for (int i = 0; i <= (int)('Z' - 'A'); i++) {
or
int i = 0;
for (char myChar = 'A'; myChar <= 'Z'; myChar++) {
final String btnText = String.valueOf(myChar);
letter[i] = new JButton(btnText);
keyboard.add(letter[i]);
letter[i].addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
textArea.append(btnText);
}
});
i++;
}
Edit
Another and perhaps better way to do this is to use Actions rather than ActionListeners. For instance,...
//....
int i = 0;
for (char myChar = 'A'; myChar <= 'Z'; myChar++) {
final String btnText = String.valueOf(myChar);
MyKeyBoardAction action = new MyKeyBoardAction(btnText);
letter[i] = new JButton(action);
i++;
}
}
private class MyKeyBoardAction extends AbstractAction {
public MyKeyBoardAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
textArea.append(getValue(NAME).toString());
}
}
You also ask about the space character,
do u know how to add a space to the code as well?
That would not work with your for loop but can be added on its own.
Also,
but why avoid the numbers?
Because it's easy to make hard to fix bugs if you use "magic" numbers that don't intrinsically make sense. Also, by using constants, variables rather than hard-coded numbers, your variables make your code self-commenting.
Why don't create direct the action listener?
letter[i].addActionListener = (new ActionListener() {
public void onClick(View view) {
textArea.append(""+(char)('A'+ i));
}
);
So I'm trying to implement a guess and check game where there are letters of the English alphabet displayed then each time one is clicked it goes away if it wasn't part of the word specified. (I guess you can call it hangman but there's going to be more to it). Then once the game is lost setLetters is called again and should reset all the letters back onto the screen. (that's another problem i haven't solved in this and know belongs between the update and setLetters() )
letters = new JLabel[26];
for (int i = 0; i < 26; i++) {
int ch = 'A' + i;
letters[i] = new JLabel("" + (char) ch);
panel.add(letters[i],c);
}
ctrl.setLetters(letters);
in the ctrl class there is
public void setLetters(JLabel[] letters2) {
letterLabel = letters2;
for (int i = 0; i < letters2.length; i++) {
letterLabel[i].addMouseListener(this);
/* i'm assuming this issue is here with adding tons of mouselisteners to the label each time it resets, but i'm not sure how to fix it */
}
}
#Override
public void mousePressed(MouseEvent e) {
for (int i = 0; i < 26; i++) {
if (e.getSource() == letterLabel[i]) {
if (!game.letterAvailable((letterLabel[i].getText().charAt(0)))){
letterLabel[i].setText(" ");
}
else {
game.makeGuess((letterLabel[i].getText().charAt(0)));
}
}
}
update();
}
public void update() {
if (letterLabel != null) {
setLetters(letterLabel);
}
if (panel != null) { // redraw (not important for this error)
panel.repaint();
};
}
the error I get is exponentially that of:
java.awt.AWTEventMulticaster.mouseExited(Unknown Source) OR
java.awt.AWTEventMulticaster.mouseExited(Unknown Source)
depending on how the letters are clicked (have yet to figure out why)
As stated in this answer from HoverCraftFullOfEeels
Don't add the same listener to a component inside of its listener code.
In you're mousePressed you're calling update() which calls setLetter, which adds a listener to the component.
Looks like you will have to do some refactoring. Can't really help you any further without full understanding what you're trying to accomplish.
Below is my action class,the program can run, but sometimes when you pressed the button, it continuously shows pressed status,and the result can't display in the JTextField?
public class SsqAction implements ActionListener{
String luckNum="";
private JTextField luckField;
public SsqAction(JTextField luckField){
this.luckField=luckField;
}
public void actionPerformed(ActionEvent event){
int[] a={0,0,0,0,0,0,0};
int tag=0;
for(int k=0;k<6;){
double i=Math.random()*33;
int temp=(int)Math.ceil(i);
a[k]=temp;
for(int j=0;j<k;j++){
if(a[j]==a[k]){
tag=1;
break;
}
}
if(tag==0){
k++;
}
}
double j=Math.random()*16;
int b=(int)Math.ceil(j);
a[6]=b;
for(int i=0;i<7;i++){
luckNum=luckNum+a[i]+" ";
}
luckField.setText(luckNum);
}
}
In your for loop
for(int k=0;k<6;)
You don't increment k. You increment it here:
if(tag==0){
k++;
}
But, tag might be set to 1 here:
if(a[j]==a[k]){
tag=1;
break;
}
(break breaks the inner loop, not the external one)
Now, tag is 1 forever, and k will never be incremented → Infinite loop.
Tip: Use a debugger, he's your best friend.