Make a Swing Timer execute N times? - java

How can I adjust this Timer code so that it executes four times and then stops?
timer = new Timer(1250, new java.awt.event.ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent e) {
System.out.println("Say hello");
}
});
timer.start();

You could do:
Timer timercasovac = new Timer(1250, new ActionListener() {
private int counter;
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Say hello");
counter++;
if (counter == 4) {
((Timer)e.getSource()).stop();
}
}
});
timercasovac.start();

You need to count yourself and then stop the Timer manually:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TestTimer {
private int count = 0;
private Timer timer;
private JLabel label;
private void initUI() {
JFrame frame = new JFrame("test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
label = new JLabel(String.valueOf(count));
frame.add(label);
frame.pack();
frame.setVisible(true);
timer = new Timer(1250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (count < 4) {
count++;
label.setText(String.valueOf(count));
} else {
timer.stop();
}
}
});
timer.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestTimer().initUI();
}
});
}
}

Related

How to make jButton.setVisible() work instantaneously?

I've written a test program with making the jButton invisible and visible:
import java.awt.Dimension;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.event.*;
public class Blink
{
private JButton btn;
private static JFrame f;
public static void delay(int ms)
{
try
{
Thread.sleep(ms);
}
catch(InterruptedException ex)
{
Thread.currentThread().interrupt();
}
}
public Blink()
{
f = new JFrame("Blink");
f.setPreferredSize(new Dimension(500, 500));
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
btn = new JButton("Click me and I'll blink!");
f.add(btn);
btn.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
buttonClicked();
}
});
f.pack();
f.setVisible(true);
}
private void buttonClicked()
{
for (int i = 0; i < 5; i++)
{
delay(300);
btn.setVisible(false);
delay(300);
btn.setVisible(true);
}
}
public static void main(String[] args)
{
new Blink();
}
}
Unfortunately, the jButton does not blink. And when the buttonClicked() function is changed, so that the jButton is set invisible 5 times and is not set visible back, the jButton disappears only when the for-loop finishes. How to make the jButton disappear an reappear instantaneously?
You cannot use Thread.sleep method in Swing Thread (all listeners are called in Event Dispatcher Thread - EDT). To achieve blinking you must use javax.swing.Timer class. For more information look here and here
Here is your reworked example:
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.SwingUtilities;
import javax.swing.Timer;
public class Blink {
private JButton btn;
private JFrame f;
public void delay(int ms, boolean show) {
Timer timer = new Timer(ms, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
btn.setVisible(show);
btn.getParent().revalidate();
btn.getParent().repaint();
}
});
timer.setRepeats(false);
timer.start();
}
public Blink() {
f = new JFrame("Blink");
f.setPreferredSize(new Dimension(500, 500));
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
btn = new JButton("Click me and I'll blink!");
f.add(btn);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
buttonClicked();
}
});
f.pack();
f.setVisible(true);
}
private void buttonClicked() {
for (int i = 1; i <= 10; i += 2) {
delay(300 * i, false);
delay(300 * (i + 1), true);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Blink();
}
});
}
}
For some complicated layouts, call setVisible(false) may have side-effects. In this case the CardLayout with your component and an empty panel should be used.
Here is the variant with CardLayout
import java.awt.CardLayout;
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.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Blink {
private static final String BUTTON_CARD = "button";
private static final String EMPTY_CARD = "empty";
private JButton btn;
private JFrame f;
private final CardLayout cardLayout = new CardLayout();
public void delay(int ms, boolean show) {
Timer timer = new Timer(ms, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
cardLayout.show(btn.getParent(), show ? BUTTON_CARD : EMPTY_CARD);
btn.getParent().revalidate();
btn.getParent().repaint();
}
});
timer.setRepeats(false);
timer.start();
}
public Blink() {
f = new JFrame("Blink");
f.setPreferredSize(new Dimension(500, 500));
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(cardLayout);
btn = new JButton("Click me and I'll blink!");
f.add(btn, BUTTON_CARD);
f.add(new JPanel(), EMPTY_CARD);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
buttonClicked();
}
});
f.pack();
f.setVisible(true);
}
private void buttonClicked() {
for (int i = 1; i <= 10; i += 2) {
delay(300 * i, false);
delay(300 * (i + 1), true);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Blink();
}
});
}
}
As #Sergiy points out - make sure you're running from the EDT, and don't sleep on the EDT, use a swing timer instead.
To make your jButton appear "invisbile", you can do something like this:
public void setInvisible(jButton jb) {
jb.setOpaque(false);
jb.setContentAreaFilled(false);
jb.setBorderPainted(false);
jb.setText("");
}
// Assuming you have the original text saved in a variable
public void setRevisible(jButton jb) {
jb.setOpaque(true);
jb.setContentAreaFilled(true);
jb.setBorderPainted(true);
jb.setText(originalString);
}
Depending on if you want the button to be clickable when it's invisible, you can also add btn.setEnabled(bool);

Refresh jTextField following a timer

I'm working on an assignment for Java subject. I'm using NetBean IDE. My assignment requests me to make a word game. The game I'm designing involves a timer with delay of 1000 ms. The timer decrements a variable from 30 to 0. The timer itself is working. It is placed in the main function of GUI class. The problem I'm facing that I don't know how I'm supposed to update a jTextfield with everytime the variable is decremented.
public static void main(String args[]) {
Time counter=new Time();
ActionListener actListner = new ActionListener() {
public void actionPerformed(ActionEvent event) {
counter.decTime();
jTime.setText("Time left: " + counter.getTime());
}
};
Timer timer = new Timer(1000, actListner);
timer.start();
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new StartGUI().setVisible(true);
}
});
}
I'm not sure how to implement this properly
jTime.setText("Time left: " + counter.getTime());
Not sure what you're doing wrong (that's why you should always provide a short example that we can copy-paste-compile-run that demonstrates the problem. When I make the code runnable, it works fine. That's why we need to be able to run your code to see where you're going wrong.
Here's the runnable version:
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.Timer;
public class StartGUI extends JFrame {
static JTextField jTime = new JTextField(10);
public StartGUI() {
jTime.setEditable(false);
add(jTime);
setLayout(new GridBagLayout());
setSize(200, 200);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setLocationRelativeTo(null);
}
static class Time {
int time = 1000;
void decTime() {
time--;
}
int getTime() {
return time;
}
}
public static void main(String args[]) {
Time counter = new Time();
ActionListener actListner = new ActionListener() {
public void actionPerformed(ActionEvent event) {
counter.decTime();
jTime.setText("Time left: " + counter.getTime());
}
};
Timer timer = new Timer(1000, actListner);
timer.start();
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new StartGUI().setVisible(true);
}
});
}
}
Here is the code refactored a bit with some better practices
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.Timer;
public class StartGUI extends JFrame {
private JTextField jTime = new JTextField(10);
private Timer timer = createTimer(1000);
public StartGUI() {
jTime.setEditable(false);
add(jTime);
setLayout(new GridBagLayout());
pack();
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setLocationRelativeTo(null);
}
private Timer createTimer(int delay) {
Timer timer = new Timer(delay, new ActionListener(){
Time counter = new Time(30);
public void actionPerformed(ActionEvent e) {
if (counter.getTime() == 0) {
((Timer)e.getSource()).stop();
jTime.setText("Times up!");
} else {
jTime.setText("Time left: " + counter.getTime());
counter.decTime();
}
}
});
timer.setInitialDelay(0);
return timer;
}
private Timer getTimer() {
return timer;
}
static class Time {
int time = 1000;
public Time(int time) {
this.time = time;
}
void decTime() {
time--;
}
int getTime() {
return time;
}
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
StartGUI start = new StartGUI();
start.setVisible(true);
start.getTimer().start();
}
});
}
}

timer t = new timer(50, b); t.stop(); is not working

My t.stop(); method is not working. I am going crazy trying to figure out why my stop method is not working.
I'm using the a timer in my code and I can't get it to stop. Can anyone take a look at it and tell me what's going on?:
/*Gilberto Rose*/
package homework2;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class MultipleBalls extends JFrame implements ActionListener
{
int dx = 2;
int dy = 2;
int x = 1;
int y = 1;
int i = 0;
public static void main(String[] args)
{
Runnable balls = new Ball2();
Thread thread1 = new Thread(balls);
thread1.run();
}
#Override
public void actionPerformed(ActionEvent arg0)
{
repaint();
System.out.println(i++);
}
}// End of Ball class
class Ball2 extends JPanel implements Runnable
{
MultipleBalls b = new MultipleBalls();
JButton g = new JButton("resume");
JButton f = new JButton("suspend");
JButton e = new JButton("-1");
JButton d = new JButton("+1");
List<Ball2> L = new ArrayList<Ball2>();
Timer t = new Timer(50, b);
public int x = 6;
public void loopstop()
{
t.stop();
}// end of loopstop method
Ball2()
{
controller4();
controller3();
controller2();
controller();
add(d);
add(e);
add(f);
add(g);
}// End of Ball2 constructor
public void run()
{
Ball2 c = new Ball2();
b.setSize(500, 500);
b.setVisible(true);
b.add(c);
t.start();
} // End of run method
public void controller()
{
d.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
L.add(new Ball2());
}
});
}// End of controller
public void controller2()
{
e.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
System.out.println("subtracter");
L.remove(L.size()-1);
}
});
}// End of controller2
public void controller3()
{
f.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
loopstop();
}
});
}// End of controller3
public void controller4()
{
g.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
System.out.println("Resume");
}
});
}// End of controller4
public void paintComponent(Graphics g)
{
if(L.size() > 0)
{
int i = 0;
do
{
g.fillOval(L.get(i).ballx(), L.get(i).bally(), 90, 90);
i++;
}while(i < L.size() && true ); // End of Do while loop
}// End of if statement
}// End of paintComponent
MultipleBalls bb = new MultipleBalls();
public int ballx()
{
if (bb.x == 0 || bb.x == 500)
{
bb.dx *= -1;
} // End of if statement
bb.x += bb.dx;
return bb.x;
}
public int bally()
{
if (bb.y == 0 || bb.y == 500 )
{
bb.dy *= -1;
}// end of if statement
bb.y += bb.dy;
return bb.y;
}// End of bally
}// End of Ball2 class
Your code is extremely convoluted, I believe that it's suffering from something called cyclomatic complexity, so much so, it is difficult for you or us to see what object is creating what other object, and what is running what. And this is your problem. You have at least two MultipleBall objects, two Ball2 objects, and you're starting the Timer for one of the Ball2 objects and stopping it for the other.
The solution: simplify this code greatly.
Create one MultipleBalls object, just one.
Don't have MultipleBalls implement ActionListener. Rather use an anonymous inner class for your ActionListener and create it on the spot where you need it.
Create just one Ball2 object, just one.
Also note that you almost never call run() on a Thread object but rather start(), but having said that, I'm not even sure that you should be using a Thread object where you're using it.
Edit
My main class would be simple, and would simply have a main method and supporting method that gets things started. Something like:
public class MultipleBalls {
private static void createAndShowGui() {
BallsPanel mainPanel = new BallsPanel();
JFrame frame = new JFrame("Multiple Balls");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Edit
For an example of a separation of concerns:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.*;
public class MultipleBallsZ {
private static void createAndShowGui() {
BallsPanelZ ballsPanel = new BallsPanelZ();
new Control(ballsPanel);
JFrame frame = new JFrame("Multiple Balls");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(ballsPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class BallsPanelZ extends JPanel {
private static final int TIMER_DELAY = 200;
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private Timer timer = new Timer(TIMER_DELAY, new TimerListener());
private int counter = 0;
private Control control = null;
public BallsPanelZ() {
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public Timer getTimer() {
return timer;
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
counter++;
System.out.printf("Count: %03d%n", counter);
}
}
public void setControl(Control control) {
this.control = control;
for (Action action : control) {
add(new JButton(action));
}
}
}
#SuppressWarnings("serial")
class Control implements Iterable<Action> {
private List<Action> actionList = new ArrayList<>();
private BallsPanelZ ballsPanel;
public Control(BallsPanelZ ballsPanel) {
actionList.add(new PauseAction());
actionList.add(new ResumeAction());
this.ballsPanel = ballsPanel;
ballsPanel.setControl(this);
}
private class PauseAction extends AbstractAction {
public PauseAction() {
super ("Timer Pause");
putValue(MNEMONIC_KEY, KeyEvent.VK_P);
}
#Override
public void actionPerformed(ActionEvent e) {
ballsPanel.getTimer().stop();
}
}
private class ResumeAction extends AbstractAction {
public ResumeAction() {
super("Timer Resume");
putValue(MNEMONIC_KEY, KeyEvent.VK_R);
putValue(DISPLAYED_MNEMONIC_INDEX_KEY, 6);
}
#Override
public void actionPerformed(ActionEvent e) {
ballsPanel.getTimer().restart();
}
}
#Override
public Iterator<Action> iterator() {
return actionList.iterator();
}
}

How to stop Swing timer inside the anonymous class?

Im making a guessing game but
Im having a problem in Swing timer, because I cant stop it when i putted an IF statement.
This is the part of my code where I encountered the problem.
continueButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
f.add(firstPicblur);
f.invalidate();
f.remove(loadingEffectBtn);
f.setVisible(true);
f.repaint();
Timer tt = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
f.add(firstPiclabelA,BorderLayout.NORTH);
f.invalidate();
f.remove(loadingEffect);
f.setVisible(true);
f.repaint();
score01.setText("Score: " + gScore);
gScore--;
}
});
tt.start();
tt.setRepeats(true);
if(gScore == 980){
tt.stop();
P.S this is the last problem that im solving in my Guessing Game, after this everything will be OK.
The Timer is the source of the action event, so you can use it, if you don't want to make it a field of the class:
#Override
public void actionPerformed(ActionEvent e) {
...
gscore--;
if (gScore == 980) {
((Timer) e.getSource()).stop();
}
}
I've mislead you in the comments. Instead of making tt final, make it a member of the enclosing class. Here's an example:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TimerTest extends JFrame {
private JLabel label = new JLabel("default");
private Timer timer;
private int gScore = 985;
public TimerTest() {
add(label);
pack();
timer = new Timer(100, new ActionListener() {
public void actionPerformed(ActionEvent e) {
gScore--;
if (gScore == 980) {
timer.stop();
}
label.setText(String.valueOf(gScore));
}
});
timer.setRepeats(true);
timer.start();
setLocationRelativeTo(null);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
TimerTest test = new TimerTest();
test.setVisible(true);
}
});
}
}
You need to call it's constructor only once, and then rely on it's start() and stop() methods, so your code should look similar to this:
continueButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
f.add(firstPicblur);
f.invalidate();
f.remove(loadingEffectBtn);
f.setVisible(true);
f.repaint();
tt.start();
}

Button ActionListener

Ok, so I made a simple program that adds the value to counter each time a button is clicked.
Now, I would like to add "Auto" button feature to increase the value of the counter when the "Auto" button is clicked. I'm having problems with it because it won't render each counter value on the screen, instead the value updates when the loop is done.. Here is my code:
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.TimeUnit;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Gui extends JFrame{
private static final long serialVersionUID = 1L;
private JButton uselesButton;
private JButton autoButton;
private FlowLayout layout;
private long counter = 0;
public Gui() {
super("Button");
layout = new FlowLayout(FlowLayout.CENTER);
this.setLayout(layout);
uselesButton = new JButton(String.format("Pressed %d times", counter));
add(uselesButton);
uselesButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
counter++;
uselesButton.setText(String.format("Pressed %d times", counter));
}
});
autoButton = new JButton("Auto");
add(autoButton);
autoButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for(long i =0; i < 99999999;i++) {
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e1) {
System.out.println("ERROR");
}
counter = i;
uselesButton.setText(String.format("Pressed %d times", counter));
}
}
});
}
}
Keep in mind that I'm a beginner... All help appreciated :)
Take a look at the tutorial about How to Use Swing Timer and then look at my solution:
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Gui extends JFrame {
private static final long serialVersionUID = 1L;
private JButton uselesButton;
private JButton autoButton;
private FlowLayout layout;
private long counter = 0;
private javax.swing.Timer timer;
public Gui() {
super("Button");
layout = new FlowLayout(FlowLayout.CENTER);
setLayout(layout);
setDefaultCloseOperation(3);
setSize(300, 300);
setLocationRelativeTo(null);
//initialing swing timer
timer = new javax.swing.Timer(100, getButtonAction());
autoButton = new JButton("Auto");
add(autoButton);
autoButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (!timer.isRunning()) {
timer.start();
} else {
timer.stop();
}
}
});
}
private ActionListener getButtonAction() {
ActionListener action = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
autoButton.setText(String.format("Pressed %d times", ++counter));
if (counter > 1000) {
timer.stop();
}
}
};
return action;
}
public static void main(String... args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Gui().setVisible(true);
}
});
}
}
your code block the GUI thread (EDT) when enter inside this loop (GUI will hang, the button will not update until you finish), so you should add your code inside another worker thread:
autoButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new Thread(new Runnable() {
#Override
public void run() {
for(long i =0; i < 99999999;i++) {
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e1) {
System.out.println("ERROR");
}
counter = i;
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
uselesButton.setText(String.format("Pressed %d times", counter));
}
});
}
}
}).start();
}
});
the problem here is that the system is in the loop, so it can't paint the changes.
in order to do that you need to open a new thread. the new thread will do the loop, and the main thread will repaint the form.
one more thing, you shouldn't do sleep on the main thread. you can use a timer that will tick every 10 millisecond instead of sleep(10)
here is an example

Categories