I have to make a countdown program which also shows the tenths of a second;
for example from a 10.0 second countdown, it should display 9.9s, 9.8s, ... 0.0s
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
timer.start();
timer2.start();
}
Double timeLeft=5000; //5 seconds
Timer timer=new Timer(1,countDown);
Timer timer2=new Timer(1000,countDown2);
ActionListener countDown=new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
timeLeft--;
SimpleDateFormat df=new SimpleDateFormat("mm:ss:S");
jLabel1.setText(df.format(timeLeft));
if(timeLeft<=0)
{
timer.stop();
}
}
};
what happens is it's taking more than 5 seconds to finish the 5 seconds.
I compared the code above with another Timer
int timeLeft2=5;
ActionListener countDown2=new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
timeLeft2--;
jLabel2.setText(String.valueOf(timeLeft2));
if(timeLeft2<=0)
{
time2.stop();
}
}
};
is it natural that they don't get the same?
The time between ticks (the time when actionPerfomed is called) is variable, it only guarantees that it will be at least n milliseconds
Instead of relying on some counter, which could become unreliable over time, you should try and calculate the difference between the time the Timer was started and the current time, for example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.SimpleDateFormat;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class CountDown {
public static void main(String[] args) {
new CountDown();
}
public CountDown() {
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 Timer timer;
private long startTime = -1;
private long duration = 5000;
private JLabel label;
public TestPane() {
setLayout(new GridBagLayout());
timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (startTime < 0) {
startTime = System.currentTimeMillis();
}
long now = System.currentTimeMillis();
long clockTime = now - startTime;
if (clockTime >= duration) {
clockTime = duration;
timer.stop();
}
SimpleDateFormat df = new SimpleDateFormat("mm:ss:SSS");
label.setText(df.format(duration - clockTime));
}
});
timer.setInitialDelay(0);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (!timer.isRunning()) {
startTime = -1;
timer.start();
}
}
});
label = new JLabel("...");
add(label);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
Updating the label probably takes more that 1ms, which is why it can't keep up. If you only need to display tenths of a second, simply have your timer update less often.
ActionListener countDown=new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
timeLeft -= 100;
SimpleDateFormat df=new SimpleDateFormat("mm:ss:S");
jLabel1.setText(df.format(timeLeft));
if(timeLeft<=0)
{
timer.stop();
}
}
};
Timer timer=new Timer(100, countdown);
Related
I have programmed a simple stopwatch with three functionalities. First, I have a start button to begin the stopwatch , a pause button to pause the stopwatch and finally a reset button to reset the entire stopwatch.
When I hit the pause button, the stopwatch pauses, say at 10.0 seconds. When I resume the stopwatch (pressing the Start button again), the stopwatch doesn't resume from 10.0 seconds onwards. It resumes from the amount of time I paused and the current time. For example, if I paused for 5 seconds and hit resume, the stopwatch goes from 15.0 seconds onwards.
I am aware there isn't a actual pause function in Swing.Timer. Would there be a way to tackle this so the stopwatch resumes normally?
Any suggestion would be appreciated.
Code:
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.Instant;
import javax.swing.Timer;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class GuiStopwatch {
public static void main(String[] args) {
JFrame frame = new JFrame("Stopwatch");
frame.setSize(500, 500);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setVisible(true);
JPanel panel = new JPanel();
panel.setLayout(null);
JButton startbtn = new JButton("START");
JButton pausebtn = new JButton("PAUSE");
JButton reset = new JButton("RESET");
JLabel time = new JLabel("Time shows here");
panel.add(startbtn);
panel.add(pausebtn);
panel.add(reset);
panel.add(time);
startbtn.setBounds(50, 150, 100, 35);
pausebtn.setBounds(50, 200, 100, 35);
reset.setBounds(50, 250, 100, 35);
time.setBounds(50, 350, 100, 35);
time.setBackground(Color.black);
time.setForeground(Color.red);
frame.add(panel);
Timer timer = new Timer(1,new ActionListener() {
Instant start = Instant.now();
#Override
public void actionPerformed(ActionEvent e) {
time.setText( Duration.between(start, Instant.now()).getSeconds() + ":" + Duration.between(start, Instant.now()).getNano() );
}
});
startbtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
pausebtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
timer.stop();
}
});
reset.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
time.setText("0:0");
}
});
Conceptually, the idea is, you want to keep track of the "total running" of the stop watch, this is, all the total duration it has been active.
There's a number of ways you might achieve this, one might be to simply keep a running total which is only updated when the stop watch is stopped or paused. The "duration" of the stop watch is then a sum of the "current duration" of the "current" cycle and the "total previous" duration
Something like...
public class StopWatch {
private LocalDateTime startTime;
private Duration totalRunTime = Duration.ZERO;
public void start() {
startTime = LocalDateTime.now();
}
public void stop() {
Duration runTime = Duration.between(startTime, LocalDateTime.now());
totalRunTime = totalRunTime.plus(runTime);
startTime = null;
}
public void pause() {
stop();
}
public void resume() {
start();
}
public void reset() {
stop();
totalRunTime = Duration.ZERO;
}
public boolean isRunning() {
return startTime != null;
}
public Duration getDuration() {
Duration currentDuration = Duration.ZERO;
currentDuration = currentDuration.plus(totalRunTime);
if (isRunning()) {
Duration runTime = Duration.between(startTime, LocalDateTime.now());
currentDuration = currentDuration.plus(runTime);
}
return currentDuration;
}
}
Okay, so start and stop are essentially the same as pause and resume, but you get the point.
And, a runnable example...
Now, this example runs a Swing Timer constantly, but the StopWatch can paused and resumed at any time, the point is to demonstrate that the StopWatch is actually working correctly ;)
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.LocalDateTime;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) throws InterruptedException {
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 JLabel label;
private JButton btn;
private StopWatch stopWatch = new StopWatch();
private Timer timer;
public TestPane() {
label = new JLabel("...");
btn = new JButton("Start");
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(label, gbc);
add(btn, gbc);
timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setText(Long.toString(stopWatch.getDuration().getSeconds()));
}
});
timer.start();
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (stopWatch.isRunning()) {
stopWatch.pause();
btn.setText("Start");
} else {
stopWatch.resume();
btn.setText("Pause");
}
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.dispose();
}
}
public class StopWatch {
private LocalDateTime startTime;
private Duration totalRunTime = Duration.ZERO;
public void start() {
startTime = LocalDateTime.now();
}
public void stop() {
Duration runTime = Duration.between(startTime, LocalDateTime.now());
totalRunTime = totalRunTime.plus(runTime);
startTime = null;
}
public void pause() {
stop();
}
public void resume() {
start();
}
public void reset() {
stop();
totalRunTime = Duration.ZERO;
}
public boolean isRunning() {
return startTime != null;
}
public Duration getDuration() {
Duration currentDuration = Duration.ZERO;
currentDuration = currentDuration.plus(totalRunTime);
if (isRunning()) {
Duration runTime = Duration.between(startTime, LocalDateTime.now());
currentDuration = currentDuration.plus(runTime);
}
return currentDuration;
}
}
}
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();
}
});
}
}
I am creating a Java Swing game and before every new game I would like to have a frame show up and countdown from 5 to 0 seconds.
While this is happening I would like the game in the background to wait until the countdown is complete. What would be the best way to make the game in the background wait?
I have tried Thread.sleep but this causes the The Event Dispatch Thread to sleep and the GUI not to update. However, it works the first time i run it but not the second.
Thankful for your help.
public class CountdownPresenterPanel {
JFrame mainFrame;
int currentNumber = 5;
JLabel textLabel;
CountdownPresenterPanel() {
mainFrame = new JFrame();
textLabel = new JLabel(String.valueOf(currentNumber), SwingConstants.CENTER);
textLabel.setFont(new Font("Arial", Font.BOLD, 55));
textLabel.setVerticalAlignment(SwingConstants.CENTER);
mainFrame.add(textLabel);
mainFrame.setUndecorated(true); // Remove bar including close, minimize
mainFrame.setSize(600,300);
mainFrame.setLocationRelativeTo(null);
mainFrame.setVisible(true);
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(currentNumber > 0) {
currentNumber--;
textLabel.setText(String.valueOf(currentNumber));
} else {
mainFrame.setVisible(false);
mainFrame.dispose();
}
}
});
timer.setRepeats(true);
timer.start();
}
}
Solution
import java.awt.*;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.Timer;
public class CountdownPresenterPanel {
private int currentNumber = 5;
private JLabel textLabel;
private final JDialog dialog;
CountdownPresenterPanel() {
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(currentNumber > 0) {
currentNumber--;
textLabel.setText(String.valueOf(currentNumber));
} else {
dialog.dispose();
}
}
});
Frame window = new Frame();
dialog = new JDialog(window, "Alert", true);
textLabel = new JLabel(String.valueOf(currentNumber), SwingConstants.CENTER);
textLabel.setFont(new Font("Arial", Font.BOLD, 55)); // Increase the font-size
dialog.add(textLabel);
dialog.setSize(400, 200);
dialog.setLocationRelativeTo(null);
timer.setRepeats(true);
timer.start();
dialog.setUndecorated(true);
dialog.setVisible(true);
}
}
Don't use a JFrame, use some kind of modal dialog
Take a look at How to Make Dialogs for more details
Seems that you want something like a SplashScreen. Have you looked to the SplasScreen Tutorial from Oracle? There Thread.sleep() is used safely and it updates the graphics correctly. I think it to be your unique solution. The only way to let the undergoing application to wait is to use the same thread. The reason your Timer didn't work is because it creates another thread to run itself.
Another option is to use a SwingWorker:
import java.awt.*;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.beans.*;
import javax.swing.*;
public class CountdownPresenterPanel2 {
public static void main(String[] args) {
final JFrame frame = new JFrame();
final JDialog splashScreen = new JDialog(null, Dialog.ModalityType.DOCUMENT_MODAL);
final JLabel textLabel = new JLabel(String.valueOf(5), SwingConstants.CENTER);
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
textLabel.setFont(new Font("Arial", Font.BOLD, 55));
textLabel.setVerticalAlignment(SwingConstants.CENTER);
splashScreen.setUndecorated(true);
splashScreen.getContentPane().add(textLabel);
splashScreen.setSize(600, 300);
splashScreen.setLocationRelativeTo(null);
splashScreen.setVisible(true);
}
});
(new GamePanelInitTask() {
#Override protected void process(java.util.List<Integer> chunks) {
for (Integer value : chunks) {
textLabel.setText(value.toString());
}
}
#Override public void done() {
splashScreen.dispose();
try {
MainGamePanel p = get();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(p);
frame.setSize(320, 240);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
}
}
}).execute();
}
}
class GamePanelInitTask extends SwingWorker<MainGamePanel, Integer> {
#Override public MainGamePanel doInBackground() {
int currentNumber = 5;
MainGamePanel game = new MainGamePanel();
while (currentNumber > 0 && !isCancelled()) {
currentNumber--;
publish(currentNumber);
game.add(new JLabel(String.format("Label: %d", currentNumber)));
try {
Thread.sleep(1000); //dummy task
} catch (InterruptedException ie) {
ie.printStackTrace();
return null;
}
}
return game;
}
}
class MainGamePanel extends JPanel {
public MainGamePanel() {
super();
try {
Thread.sleep(1000); //dummy task
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
How would I go about closing a JFrame after user inactivity?
So far I have,
Thread.sleep(10000);
I would greatly appreciate it if someone could give me code to do this?
I am new to Java and want to know more about system security
This is an example of Braj's idea using a javax.swing.Timer.
It simplifies the process, as you don't need to monitor the time between events and ensures that when the timer is triggered, the event occurs within the Event Dispatching Thread, further reducing the complexity.
Also note that I included the AWTEvent.MOUSE_MOTION_EVENT_MASK and AWTEvent.MOUSE_WHEEL_EVENT_MASK events for good measure ;)
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.AWTEventListener;
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;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class AutoClose {
public static void main(String[] args) {
new AutoClose();
}
private Timer timer;
private JLabel label;
private JFrame frame;
public AutoClose() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
label = new JLabel("Waiting...");
frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(label);
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
private int count;
#Override
public void eventDispatched(AWTEvent event) {
Object source = event.getSource();
if (source instanceof Component) {
Component comp = (Component) source;
Window win = null;
if (comp instanceof Window) {
win = (Window) comp;
} else {
win = SwingUtilities.windowForComponent(comp);
}
if (win == frame) {
timer.restart();
label.setText("Interrupted..." + (++count));
}
}
}
}, AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_WHEEL_EVENT_MASK);
timer = new Timer(5000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
frame.dispose();
}
});
// You could use a WindowListener to start this
timer.start();
}
});
}
}
Try this one
Steps to follow:
Its listening for key event as well as mouse event.
A new thread is started that will check.
If the time difference is more than specified time (10 sec in below sample code) then dispose the window.
That's all.
Here is the sample code:
private long time;
...
long eventMask = AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK;
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
public void eventDispatched(AWTEvent e) {
time = System.currentTimeMillis();
}
}, eventMask);
time = System.currentTimeMillis();
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
if (System.currentTimeMillis() - time > 10000) {
widnow.dispose();
break;
}
}
}
}).start();
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();
}
});
}
}