I've recently made a small puzzle game that deals with clicking certain areas. I also made a solver which activates the necessary areas to win. The problem that I am facing is that I would like to pause each time it activates an area to create a sort of "solving animation". My problem is represented here
package experiment;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class ExperimentHere extends JFrame implements ActionListener
{
private static final long serialVersionUID = 1L;
private JButton changeLabelButton;
private JPanel mainPanel;
private JLabel labelToChange;
public ExperimentHere() {
changeLabelButton = new JButton("Set the label");
changeLabelButton.addActionListener(this);
mainPanel = new JPanel();
labelToChange = new JLabel();
labelToChange.setText("This needs to be changed");
mainPanel.add(labelToChange);
mainPanel.add(changeLabelButton);
this.add(mainPanel);
setTitle("Timer Program");
setContentPane(mainPanel);
setPreferredSize(new Dimension(1000, 1000));
pack();
}
public void actionPerformed(ActionEvent e) {
if (e.getSource().equals(changeLabelButton)){
changeLabel();
}
}
public void changeLabel(){
for (int i = 0; i<5; i++){
labelToChange.setText(""+i);
// Pause for 200ms here
}
}
public static void main(String[] args){
ExperimentHere test = new ExperimentHere();
test.setVisible(true);
}
}
I have tried using Timers, but I'm not sure how to format it properly so that it only pauses each time the loop inside of changeLabel() is incremented, because the second paramter in Timer() asks for an ActionListener.
I've also tried using Thread.sleep() but it just freezes my program and then instantly solves it.
Ideally the changeLabel method would increment by 1, set the label to the new String, wait for 200ms, and then increment again.
I have tried using Timers, but I'm not sure how to format it properly so that it only pauses each time the loop inside of changeLabel() is incremented
When you use a Timer you don't use a loop. The point of a Timer is that you start the Timer and it keeps executing until you stop the Timer.
You also don't make methods, you make an Action to invoke whenever the Timer fires.
So you need an instance variable in your class that keeps track of the number of times the Timer has fired (lets call it "timerCounter"). Then you need to create an Action to invoke every time the Timer is fired.
So you create a couple of instance variables:
int timerCounter = 0;
Action action;
Then in the constructor of your class you create an Action something like:
action = new AbstractAction()
{
#Override
public void actionPerformed(ActionEvent e)
{
labelToChange.setText("" + timerCounter);
timerCounter++;
if (timerCounter > 5)
{
Timer timer = (Timer)e.getSource();
timer.stop();
}
}
}
So now in the ActionListenerof your button you can do something like:
timerCounter = 0;
Timer timer = new Timer(200, action);
timer.start();
Related
I am working on this reaction time game that tells you to click an arrow key once the ball has turned into a different colored ball. However, I can't seem to get the image of the ball to be replaced by the other ball.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.Timer;
public class Game extends JPanel
{
private JLabel watch, main;
private ImageIcon constant, react;
final int width = 600;
final int height = 600;
private Timer replace;
private ActionListener timerListener;
public Game()
{
setPreferredSize(new Dimension(width, height));
setBackground(Color.black);
watch = new JLabel("Click Up Arrow when you see a blue ball");
watch.setForeground(Color.white);
add(watch);
constant = new ImageIcon("constantCircle.png");
main = new JLabel(constant);
replace = new Timer(3000, timerListener);
replace.setRepeats(false);
replace.start();
add(main);
}
public void actionPerformed (ActionEvent e)
{
react = new ImageIcon("reactCircle.png");
main.setIcon(react);
}
}
This is the code for my display and I wanted to use a swing timer to replace the image after 3 seconds
This is what I want it to look like before
and this is what I want it to look like after 3 seconds
You're never initializing timerListener
private ActionListener timerListener;
Inside your constructor you have to call (With Java 8 lambdas):
timerListener = e -> {
react = new ImageIcon("reactCircle.png");
main.setIcon(react);
}
Or (Java 7 and lower):
timerListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
react = new ImageIcon("reactCircle.png");
main.setIcon(react);
}
}
And don't forget to call timerListener.stop() once the timer has fired, so that you don't keep computing more times
From Andrew Thompson's comment below:
As you only want to replace the image once, call timerListener.setRepeats(false) on your constructor. Check the docs for more information about it.
I am making a bot for a game that I play, and have made a GUI so the user of the bot has some control over when the bot is active or not. To do this, I made a start/stop button that would set a variable to true. Each attack is a different class, and they extend the TimerTask class. All they have as an implementation of the run() method that TimerTask has, and they just do
Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_1); //the ".VK_1" part will be different per class
robot.keyRelease(KeyEvent.VK_1);
Ideally, the user would press the button, and TimerTasks would run, simulating keypresses that would be attacks in the game. However, the text on the button does not change, and the TimerTasks never run. Does anyone have a solution for this? My code is below. Thanks in advance!
Main-Class:
package AQWGrindBot;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Timer;
public class Main {
private static boolean playing = false;
static Timer timer = new Timer();
public static void main(String[] args) {
int speed = 1000;
final boolean[] startVar = {false};
JFrame frame = new JFrame("AQW Attack Bot");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400,100);
frame.setResizable(false);
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
JButton start = new JButton("START");
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (start.isSelected()) {
playing = !playing;
if (start.getText().equals("START")) {
start.setText("STOP");
toggleBot(true);
} else {
start.setText("START");
toggleBot(false);
}
}
}
});
frame.setLayout(new FlowLayout());
frame.add(start);
frame.setVisible(true);
}
private static void toggleBot(boolean check) {
if (check) {
timer.schedule(new AQWBotATK1(), 0, 1000);
timer.schedule(new AQWBotATK3(), 0, 3000);
timer.schedule(new AQWBotATK4(), 0, 17000);
} else {
timer.cancel();
}
}
}
P.S I am on macOS Mojave. I do not know if this is important, but there are apparently some JFrame features that do not work properly on Mac. Please tell me if I am trying to implement one of them. Also, I am using java.awt and the Robot object to simulate key presses.
You have two main problems, the first is here:
if (start.isSelected()) {
Since start is a JButton, isSelected() will never be true, and if you want this type of functionality, you should be using a JToggleButton or a JCheckBox or JRadioButton (the latter two which extend from JToggleButton) and which change the selected state on press.
So,
JToggleButton start = new JToggleButton("START");
Another option is to create your own boolean field to toggle and to test on button press, but then you don't get a visual feed back from the button as to its state. You could also I suppose use the state of the button's text, obtained via getText()
Your other problem is your use of a java.util.Timer and java.util.TimerTask as this creates code that is potentially not Swing thread-safe. It's almost always better to use javax.swing.Timer or a "Swing Timer" when creating timers for Swing GUI's since the code within the timer's ActionListener is guaranteed to be called on the Swing event thread.
This question already has answers here:
How could I add a simple delay in a Java Swing application?
(3 answers)
Closed 6 years ago.
i'm making a poker game for a uni assignment and i was wondering if there is any way to have a method that does as follows ( Note: very roughly written up code )
JTextArea textArea = new JTextArea();
public void printer(String s){
//I want to delay here, for 2 seconds each time i print to the jtextarea
textArea.append(s);
}
public void runGame(){
printer("Dealing cards...");
//I want to delay to add to an effect of the time it takes to actually deal the cards
pokerHand.setVisibility(true);
printer("What you like to do?");
//
//Code here containing running the game
//
printer("Daniel Negreanu folds");
//I want to have a delay here for the time it takes to make a decision.
printer("Phil Hellmuth folds");
Theres many many more instances i want to use this throughout my program, and just wondered if there is any way to do this.
Thanks in advance
EDIT: Not looking to use Thread.sleep() as it doesn't work well with gui.
EDIT2: I want the pokerHand.setVisibility(true), and other methods in my code to execute AFTER the delay, ( using a timer doesn't do this ).
Not looking to use Thread.sleep() as it doesn't work well with gui.
Good, use a Swing Timer instead
I want the pokerHand.setVisibility(true), and other methods in my code to execute AFTER the delay, ( using a timer doesn't do this ).
Yes it does, you're just not using it properly, but since you've not provided any actual code, I can't say "how" you're not using it properly, only from the sounds of it, you are.
Start by taking a look at How to use Swing Timers for more details
The following is very simple example, it uses a Timer to calculate and print the amount of time between the time you clicked the button.
The example updates the UI AFTER the Timer is started, but until the Timer completes, the calculation and result are not generated
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.LocalTime;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
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 JTextArea ta;
private JButton btn;
private Timer timer;
private LocalTime startTime, endTime;
public TestPane() {
setLayout(new BorderLayout());
ta = new JTextArea(10, 20);
add(new JScrollPane(ta));
btn = new JButton("Click");
add(btn, BorderLayout.SOUTH);
timer = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
endTime = LocalTime.now();
Duration duration = Duration.between(startTime, endTime);
ta.append("Ended # " + endTime + "\n");
ta.append("Took " + (duration.toMillis() / 1000) + " seconds\n");
}
});
timer.setRepeats(false);
ta.setEditable(false);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
timer.start();
startTime = LocalTime.now();
btn.setEnabled(false);
ta.append("Start # " + startTime + "\n");
}
});
}
}
}
First, your code seems to be imperative ("ask user what to do", "game loop goes here") instead of event-driven ("when this happens, do that"). Read up on event-driven programming, which is used on all GUIs, sooner rather than later.
In event-driven code, there is no big difference between "user clicks a button" and "timeout expires" - both will cause events that your code can react to. So start by rewriting your code so that, once you press a button, the "next thing" happens. Then change it again so that once a timer expires, the same "next thing" happens.
Assuming that you have the following code:
// an ActionListener for both clicks and timer-expired events
private nextThingListener = new ActionListener() {
public void actionPerformed(ActionEvent ae) {
doNextThing();
}
};
// method that does the next thing
public void doNextThing() {
// change the UI in some way
// if using timers, may also start the next timer
}
You can now associate it to a button using:
// create a button and have it do the "next thing" when clicked
JButton jb = new JButton("do next thing");
jb.addActionListener(nextThingListener);
myInterface.add(jb); // <-- display it in the interface somehow
And you can associate it to a timer as:
// launch a timer that will ring the nextThingListener every 2 seconds
new Timer(2000, nextThingListener).start();
If you want to have the timer ring only once, you can use
// launch a timer that will ring the nextThingListener after 2 seconds
Timer t = new Timer(2000, nextThingListener);
t.setRepeats(false);
t.start();
How do I make the two buttons that are displayed reset / pause the timer? The timer works but I want to change the code for the buttons so that they will change the timer instead of outputting to the console. Thank you.
CODE:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class createWindow extends JFrame implements ActionListener
{
public static void main(String[] args)
{
new createWindow();
}//end main
createWindow()
{
super("Frame");
setSize(400,70);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new FlowLayout());
show();
final JLabel time = new JLabel("Timer");
JButton reset = new JButton("Reset timer");
JButton pause = new JButton("Pause timer");
reset.setActionCommand("resetClicked");
pause.setActionCommand("pauseClicked");
reset.addActionListener(this);
pause.addActionListener(this);
add(pause);
add(time);
add(reset);
long start = System.currentTimeMillis();
while (true)
{
long timer = System.currentTimeMillis() - start;
final int seconds = (int) (timer / 1000);
String display = Integer.toString(seconds);
time.setText(display);
}//end while loop
}//end constructor
#Override
public void actionPerformed(ActionEvent e)
{
String buttonClicked = e.getActionCommand();
if(buttonClicked.equals("resetClicked"))
{
System.out.println("The reset button was clicked"); //Change to reset timer
}
else if(buttonClicked.equals("pauseClicked"))
{
System.out.println("The pause button was clicked"); //Change to pause timer
}
}//end listener
}
Don't use an infinite while loop. This blocks the EDT. Instead use a Swing Timer. This will give you control to start and stop the Timer.
Stopwatch Example
Side Notes:
Don't use JFrame.show as that method is deprecated. Use JFrame.setVisible instead. Also make this call when all components have been added to the frame.
The functionality for the JButtons is sufficiently different to warrant using separate ActionListener instances for each button.
The preferred approach is to use a JFrame instance directly rather then extending it.
Class names in Java begin with uppercase so createWindow would become CreateWindow.
below in the sample code which i wrote where the user gets an interface with 2 buttons. when the user click on start button the timer starts and when the end button is clicked the timer stops and the difference in time is displayed.
But the difference in time is not being output:(
can someone help mi.
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
public class Timer2 extends JFrame {
private JButton start;
private JButton end;
public Timer2() {
super("Test Timer");
setLayout(new FlowLayout());
start = new JButton("START");
add(start);
end = new JButton("END");
add(end);
ButtonHandler handler = new ButtonHandler();
start.addActionListener(handler);
end.addActionListener(handler);
}
private class ButtonHandler implements ActionListener {
public void actionPerformed(ActionEvent event) {
long s_time = 0;
long e_time = 0;
long diff = 0;
String name = ((JButton) event.getSource()).getText();
if (name.equals("start")) {
s_time = System.currentTimeMillis();
} else {
e_time = System.currentTimeMillis();
}
diff = (e_time - s_time) / 1000;
JOptionPane.showMessageDialog(null, diff);
}
}
public static void main(String[] args) {
Timer2 timer2 = new Timer2();
timer2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
timer2.setSize(200, 200);
timer2.setVisible(true);
}
}
You've given your buttons text in UPPER CASE but then are looking for lower case in your event handler.
You also are setting both s_time and e_time to 0 inside the actionPerformed() method which means they are set to 0 every time you click. These both need to be fields in the ButtonHandler class.
In addition, the way you have it written, the JOptionPane.showMessageDialog() will be fired when you click either button.
Edit: To solve the last problem, move your diff calculation and JOptionPane.showMessageDialog() call to inside the else block it follows; you only want it when the "end" button is pressed.
I'm assuming you want to display the result only when the user clicks "End". The way you've written it, the dialog will be displayed either way. To resolve this, move the JOptionPane.showMessageDialog(null, diff) inside the else block.
Another thing, as #Brian Roach so helpfully explains, computers are very case sensitive (e.g. "THUS" does not equal "thus"). So, make sure you're referring to the correct item.