Okay, so our assignment is to create a Guessing Game where the user inputs a number and the text foreground is supposed to change to either red if it is too high, blue if it is too low, or green if it is exact.
Our teacher has posted code that does that, and it is quite simple and I can understand it. Problem is, when I try to reformat it for what I need I get the JFrame, I get the text field where I can input it, but when I click submit it just like freezes. I suppose it has to do with how the action listener is written, but I am not sure.
Any help would be appreciate.
Here's my code, edited so just that specific part is shown"
button.addActionListener(
new ActionListener()
{
#Override
public void actionPerformed(ActionEvent arg0)
{
userInput = keyboard.next();
if(Integer.parseInt(userInput) > randomNumber)
{
tf.setForeground(Color.red);;
}
else if(Integer.parseInt(userInput) < randomNumber)
{
tf.setForeground(Color.blue);
}
else if(Integer.parseInt(userInput) == randomNumber)
{
tf.setForeground(Color.green);
}
}
}
);
}
while(true)
That's the problem. Don't block the EDT (Event Dispatch Thread). The GUI will 'freeze' when that happens. See Concurrency in Swing for details and the fix.
Related
So I am trying to run a code, open a GUI window, choose between two buttons, which set a value and then with this value continue the rest of the code.
I have seen similar questions or Tutorials, but I do not find the suitable solution for my problem.
As I have already seen, JFrame, ActionListener and ActionEvent have to be used in order to make a GUI with a button.
An Object which extends JFrame and implements ActionListener is writen in the main method.
The Problem is, that the code writen in the main method opens the GUI window and continues to run. I just want that the code waits till the user clicks a button and then continue.
A sub-solution is, to write the code that I want in the actionPerformed method but:
The GUI window remains open, after the selection of a button
It makes no sense to me to write the rest of the code in the actionPerformed method.
Or to write a while loop until a button is clicked. A more sensible solution has to exist that I am not aware of or I do not understand the way this should work.
Here is a part of the code.
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == testStringA) {
setVariableTo = "testString_a";
try {
runMethodWithNewVariable(setVariableTo);
} catch (IOException e1) {
e1.printStackTrace();
}
System.exit(0);
} else {
setVariableTo = "project";
try {
runMethodWithNewVariable(setVariableTo);
} catch (IOException e1) {
e1.printStackTrace();
}
System.exit(0);
}
}
Instead of a JFrame, why don't you use a JOptionPane (showOptionDialog) with two buttons, "string A" and "project" instead of "Yes" and "No", for example?
JOptionPanes like "show Option Dialog" are intrinsically blocking. If you put one in your main() method, execution will "wait" for the user to select something in the dialog and the dialog will return an indicator to what was selected before execution in main() continues.
At the begining of your program, show a modal JDialog to the user! You can do this using JOptionPane.show() methods, like this:
String[] buttonTexts = {"first","second"}; //create the button texts here
//display a modal dialog with your buttons (stops program until user selects a button)
int userDecision = JOptionPane.showOptionDialog(null,"title","Select a button!",JOptionPane.DEFAULT_OPTION,JOptionPane.PLAIN_MESSAGE,null,buttonTexts,buttonTexts[0]);
//check what button the user selected: stored in the userDecision
// if its the first (left to right) its 0, if its the second then the value is 1 and so on
if(userDecision == 0){
//first button was clicked, do something
} else if(userDecision == 1) {
//second button was clicked, do something
} else {
//user canceled the dialog
}
//display your main JFrame now, according to user input!
You basically have two threads running - the main thread and the GUI thread. You don't explicitly create the GUI thread but it is there.
You can use a number of techniques to synchronise these two threads. The most basic is the good old synchronized, wait and notify. Something a Semaphore can also be used. In the main thread you would create the GUI and wait until a condition is met. In the GUI thread (i.e. actionPerformed) you would notify.
I would like to know the best way to approach what I am trying to achieve, I can't figure out the logical path I should take.
I have a JTextField and a JTextButton, when input is added to the JTextField and either enter or the button is pressed, it will display on the JTextArea. Now, what I want is to choose when and what the JTextArea and Button do.
For example I want default Enter & Button to display next append text in my code. Then when a case is presented I want the JTextField to only accept either int or string and then once completed, I want it to go back to default.
I don't know if what I am trying to do is logical or best practice...
The idea behind this is, I have a story text based gui game. I want it to display text to the JTextArea and when Enter or button is pressed to display the next line of text and when in the story it requires user input, the JTextArea will look for that input.
So far I have an EventListener and ActionListener which submits what I type from JTextField to JTextArea, but that is about it.
Thanks for your assistance! I have solved my issue, not sure if this is the "Best Solution". I combined your solution with a bit of tweaking.
In this instance, buttonState is an int which can be changed throughout my code by calling a constructor "setButtonState". I could have made buttonState a static to make things easier, but thought I could keep things clean.
enterButton.addActionListener(new ActionListener()
{ //This is used so when the enter screen button is pressed, it will submit text from text field to text area.
public void actionPerformed(ActionEvent e) {
String text = inputTextField.getText();
InputTextFieldEvent event = new InputTextFieldEvent(this, text);
if (buttonState == 0) //Displays all text in JTextField to JTextArea, mostly for testing purposes.
{
if (textInputListener != null) {
textInputListener.setInputListenerOccurred(event);
}
}
if (buttonState == 1) //Only accepts string for answer
{
if (inputTextField.getText().matches("[a-zA-Z]+"))
{
textInputListener.setInputListenerOccurred(event);
}
else
{
getAppendMainTextArea("You have entered an invalid input, only letters are allowed.");
}
}
if (buttonState == 2) //Only accepts int for answer
{
if (inputTextField.getText().matches("[0-9]+"))
{
textInputListener.setInputListenerOccurred(event);
}
else
{
getAppendMainTextArea("You have entered an invalid input, only numbers are allowed.");
}
}
}
});
I have a JInternalFrame, where I wanted to display a JOptionPane when my JTable was double clicked. I looked around in the internet and found that the only way of doing it was to override mousePressed() method and this is how I did it:
tblJobs.addMouseListener (new MouseAdapter() {
#Override
public void mousePressed (MouseEvent e) {
JTable tbl = (JTable)e.getSource();
int row = tbl.rowAtPoint(e.getPoint());
if (e.getButton() == MouseEvent.BUTTON1 && row != -1) {
if (e.getClickCount() == 2) {
JOptionPane.showMessageDialog(null, "Double click detected");
}
}
}
});
The thing is, I also have a button to hide the frame, and when the frame was hidden and re-shown, I found that upon double clicking my JTable, the JOptionPane got displayed twice. The number of times the JOptionPane got displayed seemed to increase along with the number of times I hid and show the frame. My guess is, the mouseListener got called again and again when I hide and re-show my frame. Is there a way of removing the mouseListener that was added in this way? Or is there another way to stop the JOptionPane from displaying more than once? And also, if what my code was stupid in any way, feel free to tell me! :) Thanks in advance!
How about setting a Boolean calss variable hidden = true when you hide, hidden = false when you unhide ?
Set the first line in mousePressed() to if(hidden) return;
First, I know that the way I'm trying to read the input is really bad, but I've tried to use Keylistener, and it doesn't work no matter what I try. I don't know what I'm doing wrong, so this is what I'm trying at the moment. I'm trying to call a thread in Controller.java from PreControls.java. If you could help me implement a working keylistener, it would me much appreciated, but I think fixing this thread problem will work too. I have tried debugging it, and the thread doesn't seem to start.
Code in PreControls.java:
Controller C = new Controller();
C.start();
System.out.print("Thread Should be started ");
Code in Controller.java:
package Game;
public class Controller extends Thread {
public void MyShipController(){
System.out.print("Thread Is started ");
String CharIn = "";
while(SpaceInvaders.GameOn = true){
CharIn = PreControls.ReadKeyPressed.getText();
if(CharIn.equalsIgnoreCase("a")){
SpaceInvaders.MyPos[0]--;
System.out.print("Move Left ");
}else if(CharIn.equalsIgnoreCase("d")){
SpaceInvaders.MyPos[0]++;
System.out.print("Move Right");
}else if(CharIn.equalsIgnoreCase(" ")){
//Fire Bullet
}
PreControls.ReadKeyPressed.setText("");
SpaceInvaders.MyShip.setLocation(SpaceInvaders.MyPos[0], SpaceInvaders.MyPos[1]);
jp1.repaint();
}
}
}
Sorry for not providing an SSCCEE. I'd need to send my whole project, and that defeats the point of doing a project.
ReadKeyPressed is the JTextArea that I'm having the letters inputted into.
jp1 is the JFrame
I'm working in the Eclipse IDE.
EDIT: forgot to add: After I start the applet, the Console reads "Thread Should be started" only, so it is a problem with the how I made the thread, or how I'm trying to create it.
Edit 2: My End goal is to detect when a (or left arrow) is pressed and move MyShip (a JLabel) to the left by 1 position and more it to the right by 1 position if d (or right arrow) is pressed.
Alright, I've accepted Williams answer as it made my thread run. And as suggested, I'm going to look into key bindings to detect when the keys are pressed. Thanks for all the help.
This = is an assinment operator.
while(SpaceInvaders.GameOn = true)
I think you wanted this ==
while(SpaceInvaders.GameOn == true)
Or better yet, and much cleaner, just
while(SpaceInvaders.GameOn)
Also, you should use key bindings instead of KeyListener, which my give you focus problems. See this answer for an example of using key bindings. Also see the tutorial
The code you've written in method MyShipController should be placed inside the run method of your thread. The run method is what the thread actually executes within.
As you've written it now, calling start on your Controller class will start your thread, and that thread will end immediately as its run method is empty.
public class Controller extends Thread {
#Override
public void run()
//public void MyShipController(){
System.out.print("Thread Is started ");
String CharIn = "";
while(SpaceInvaders.GameOn = true){
CharIn = PreControls.ReadKeyPressed.getText();
if(CharIn.equalsIgnoreCase("a")){
SpaceInvaders.MyPos[0]--;
System.out.print("Move Left ");
}else if(CharIn.equalsIgnoreCase("d")){
SpaceInvaders.MyPos[0]++;
System.out.print("Move Right");
}else if(CharIn.equalsIgnoreCase(" ")){
//Fire Bullet
}
PreControls.ReadKeyPressed.setText("");
SpaceInvaders.MyShip.setLocation(SpaceInvaders.MyPos[0], SpaceInvaders.MyPos[1]);
jp1.repaint();
}
}
}
However, I suggest your controller class extend Runnable rather than Thread.
This is a follow up to a previous question I had. I have a Battleships game with two boards. When the user clicks on the computer board an action occurs, along these lines:
public void mouseClicked(MouseEvent e)
// Get coordinates of mouse click
if (//Set contains cell) {
/add Cell to set of attacked cells
//Determine if set contains attacked cell.
// If yes, hit, if no, miss.
checkForWinner();
The checkForWinner method determines if the game has been won yet. If it hasn't it calls a nextTurn method which changes the current turn. If the currentTurn is set to Computer, a ComputerMove() method is automatically called.
When that method finishes, it again checksforWinner, changes turn and waits for the user to click on the grid to start the cycle again.
Ideally, I'd like to have sound effects, or at the very least a pause between moves. However, no matter how I use Thread.sleep, or TimerTask, or anything else, I can't get it to function correctly.
If I use a simple Thread.sleep(500) in the CheckforWinner method, or in the ComputerMove method, all that happens is the human's go is delayed for the set amount of time. As soon as his move is executed the computer's move is completed immediately.
I know very little about threads but I assume this is because all the initiation of the bouncing back and forth between methods begins with a method in the mouse listener.
Given the set up of my system, is there a way to implement a delay without radically changing things?
Edit: May as well include the classes:
public void checkForWinner() {
if (human.isDefeated())
JOptionPane.showMessageDialog(null, computer.getName() + " wins!");
else if (computer.isDefeated())
JOptionPane.showMessageDialog(null, human.getName() + " wins!");
else
nextTurn();
}
public void nextTurn() {
if (currentTurn == computer) {
currentTurn = human;
} else {
currentTurn = computer;
computerMove();
}
}
public void computerMove() {
if (UI.currentDifficulty == battleships.UI.difficulty.EASY)
computerEasyMove();
else
computerHardMove();
}
public void computerEasyMove() {
// Bunch of code to pick a square and determine if its a hit or not.
checkForWinner();
}
Ideally, I'd like to have sound effects, or at the very least a pause between moves. However, no matter how I use Thread.sleep, or TimerTask, or anything else, I can't get it to function correctly.
You should be using a Swing Timer. Something like:
Timer timer = new Timer(1000, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
currentTurn = computer;
computerMove();
}
});
timer.setRepeats(false);
timer.start();