I've been working on this stopwatch application for a decent amount of time now but I've encountered some problems.
Problems
The stopwatch isn't starting properly probably due to the clock logic
The stopwatch isn't displaying the numbers onto my Java program properly
It would be nice if anyone were to help explain the flaws in my program and tell me why it isn't working the way I would like it to be. Much help needed and appreciated so here is my code.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Clock {
private static void createWindow() {
// Important
final int windowWidth = 800; // Window width
final int windowHeight = 300; // Window height
// Clock variables
boolean clockRunning = true; // When clock is running, this will be true
int milliseconds = 0;
int seconds = 0;
int minutes = 0;
int hours = 0;
// Create JFrame
JFrame frame = new JFrame(); // JFrame object
// Create timer text
JLabel timer = new JLabel("00:00:00");
timer.setText("00:00:00");
timer.setBounds(355, 100, 100, 40); // Button position
// JButtons
JButton startTimer = new JButton("Start the timer"); // Start timer button
JButton stopTimer = new JButton("Stop the timer"); // Stop timer button
// Event listeners
startTimer.addActionListener(new ActionListener() // Start timer
{
#Override
public void actionPerformed(ActionEvent e)
{
System.out.println("Timer has started");
}
});
stopTimer.addActionListener(new ActionListener() // Stop timer
{
#Override
public void actionPerformed(ActionEvent e)
{
System.out.println("Timer has stoped");
}
});
// Clock logic
if (clockRunning = true) {
milliseconds = 0;
seconds++;
}
if(milliseconds > 1000) {
milliseconds=0;
seconds++;
}
if(seconds > 60) {
milliseconds=0;
seconds=0;
minutes++;
}
if(minutes > 60) {
milliseconds=0;
minutes=0;
hours++;
}
timer.setText(" : " + seconds); // Milliseconds
timer.setText(" : " + milliseconds); // Seconds
timer.setText(" : " + minutes); // Minutes
timer.setText("" + hours); // Hours
// JButton Settings
startTimer.setBounds(10, 100, 200, 30); // Button position
stopTimer.setBounds(570, 100, 200, 30); // Button position
// Frame Settings
frame.setSize(windowWidth, windowHeight); // Window size
frame.setLayout(null); // Frame position
// Add
frame.add(startTimer);
frame.add(stopTimer);
frame.add(timer);
// Frame settings
frame.setVisible(true); // Make frame visible
frame.setResizable(false); // Disables maximize
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Allows the window to be closed
}
public static void main(String[] args) {
createWindow();
}
}
The stopwatch isn't displaying the numbers onto my Java program properly
timer.setText(" : " + seconds); // Milliseconds
timer.setText(" : " + milliseconds); // Seconds
timer.setText(" : " + minutes); // Minutes
timer.setText("" + hours); // Hours
The setText(…) method replaces the existing text.
So the above code is the same as:
timer.setText("" + hours); // Hours
The stopwatch isn't starting properly probably due to the clock logic
For a stop watch you will need to use a Swing Timer so you can update the text at a specified interval.
See: Program freezes during Thread.sleep() and with Timer for the basics of using a Timer.
Related
A Swing based Timer is recommended for updating GUI components - because the calls to the components are automatically on the Event Dispatch Thread (the correct thread for updating Swing or AWT based components).
The Swing Timer though, has a tendency to 'drift' off time. If you create a timer that fires every second, after an hour or so, it might have drifted a few seconds above or below the elapsed time.
When using a Swing Timer to update a display which must be accurate (e.g. a countdown timer / stop watch), how do we avoid this time drift?
The trick here is to keep track of the elapsed time, check it frequently, and update the GUI when the actual time required ('one second' in this case) has passed.
Here is an example of doing that. Pay attention to the comments in the code.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class NonDriftingCountdownTimer {
private JComponent ui = null;
private Timer timer;
private JLabel outputLabel;
NonDriftingCountdownTimer() {
initUI();
}
/** Keeps track of the start time and adjusts the count
* based on the ELAPSED time.
* This should be used with a short time between listener calls. */
class TimerActionListener implements ActionListener {
long start = -1l;
int duration;
int count = 0;
TimerActionListener(int duration) {
this.duration = duration;
}
#Override
public void actionPerformed(ActionEvent e) {
long time = System.currentTimeMillis();
if (start<0l) {
start = time;
} else {
long next = start+(count*1000);
if (time>next) {
count++;
outputLabel.setText((duration-count)+"");
if (count==duration) {
timer.stop();
JOptionPane.showMessageDialog(
outputLabel, "Time Is Up!");
}
}
}
}
}
public final void initUI() {
if (ui!=null) return;
ui = new JPanel(new BorderLayout(4,4));
ui.setBorder(new EmptyBorder(4,4,4,4));
JPanel controlPanel = new JPanel();
ui.add(controlPanel, BorderLayout.PAGE_START);
final SpinnerNumberModel durationModel =
new SpinnerNumberModel(10, 1, 1200, 1);
JSpinner spinner = new JSpinner(durationModel);
controlPanel.add(spinner);
JButton startButton = new JButton("Start");
ActionListener startListener = (ActionEvent e) -> {
int duration = durationModel.getNumber().intValue();
TimerActionListener timerActionListener =
new TimerActionListener(duration);
if (timer!=null) { timer.stop(); }
// Note the short time of fire. This will allow accuracy
// to within 1/50th of a second (without gradual drift).
timer = new Timer(20, timerActionListener);
timer.start();
};
startButton.addActionListener(startListener);
controlPanel.add(startButton);
outputLabel = new JLabel("0000", SwingConstants.TRAILING);
outputLabel.setFont(new Font(Font.MONOSPACED, Font.BOLD, 200));
ui.add(outputLabel);
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = () -> {
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
} catch (Exception useDefault) {
}
NonDriftingCountdownTimer o = new NonDriftingCountdownTimer();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
};
SwingUtilities.invokeLater(r);
}
}
I made a puzzle game in java Applet, and I need to add a timer that runs for 5 minutes where the player has to solve the puzzle within this time, if not a dialog box will appear asking to retry, so then I need the timer to start again.
Can someone tell me how can I code this.
public void init (){
String MINUTES = getParameter("minutes");
if (MINUTES != null) remaining = Integer.parseInt(MINUTES) * 600000;
else remaining = 600000; // 10 minutes by default
// Create a JLabel to display remaining time, and set some PROPERTIES.
label = new JLabel();
// label.setHorizontalAlignment(SwingConstants.CENTER );
// label.setOpaque(false); // So label draws the background color
// Now add the label to the applet. Like JFrame and JDialog, JApplet
// has a content pane that you add children to
count.add(label);
Puzframe.add(count,BorderLayout.SOUTH);
// Obtain a NumberFormat object to convert NUMBER of minutes and
// seconds to strings. Set it up to produce a leading 0 if necessary
format = NumberFormat.getNumberInstance();
format.setMinimumIntegerDigits(2); // pad with 0 if necessary
// Specify a MouseListener to handle mouse events in the applet.
// Note that the applet implements this interface itself
// Create a timer to call the actionPerformed() method immediately,
// and then every 1000 milliseconds. Note we don't START the timer yet.
timer = new Timer(1000, this);
timer.setInitialDelay(0); //
timer.start(); }
public void start() { resume(); }
//The browser calls this to stop the applet. It may be restarted later.
//The pause() method is defined below
void resume() {
// Restore the time we're counting down from and restart the timer.
lastUpdate = System.currentTimeMillis();
timer.start(); // Start the timer
}`
//Pause the countdown
void updateDisplay() {
long now = System.currentTimeMillis(); // current time in ms
long elapsed = now - lastUpdate; // ms elapsed since last update
remaining -= elapsed; // adjust remaining time
lastUpdate = now; // remember this update time
// Convert remaining milliseconds to mm:ss format and display
if (remaining < 0) remaining = 0;
int minutes = (int)(remaining/60000);
int seconds = (int)((remaining)/1000);
label.setText(format.format(minutes) + ":" + format.format(seconds));
label.setForeground(new Color(251,251,254));
label.setBackground(new Color(0,0,0));
// If we've completed the countdown beep and display new page
if (remaining == 0) {
// Stop updating now.
timer.stop();
}
count.add(label);
Puzframe.add(label,BorderLayout.SOUTH); }
This what I have so far, but my problem is that it doesn't appear in my game. I'm calling the updateDisplay() from actionPerformed
Use Swing Timer it is made for such a scenario
//javax.swing.Timer
timer = new Timer(4000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(mainFrame,
"End Of Game",
"5 minutes has passed",
JOptionPane.ERROR_MESSAGE);
}
});
I prepared a simple example to demonstrate it
Example
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class SwingControlDemo {
private JFrame mainFrame;
private JPanel controlPanel;
private Timer timer;
public SwingControlDemo(){
prepareGUI();
}
public static void main(String[] args){
SwingControlDemo swingControlDemo = new SwingControlDemo();
swingControlDemo.showEventDemo();
}
private void prepareGUI(){
mainFrame = new JFrame("Java SWING Examples");
mainFrame.setSize(400,400);
mainFrame.setLayout(new GridLayout(3, 1));
mainFrame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent windowEvent){
System.exit(0);
}
});
controlPanel = new JPanel();
controlPanel.setLayout(new FlowLayout());
mainFrame.add(controlPanel);
mainFrame.setVisible(true);
//javax.swing.Timer
timer = new Timer(4000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(mainFrame,
"End Of Game",
"5 minutes has passed",
JOptionPane.ERROR_MESSAGE);
}
});
}
private void showEventDemo(){
JButton okButton = new JButton("Start Game");
okButton.setActionCommand("OK");
okButton.addActionListener(new ButtonClickListener());
controlPanel.add(okButton);
mainFrame.setVisible(true);
}
private class ButtonClickListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
timer.start();
String command = e.getActionCommand();
if( command.equals( "OK" )) {
System.out.println("Timer started");
}
}
}
}
Hi I am trying to create a StopWatch app for one of the project questions in the Java Foundations Textbook. So far I have written this code:
package StopWatch;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class StopWatchPanel extends JPanel{
private JPanel userPanel = new JPanel();
private JLabel timeDisplay = new JLabel("00:00:00", SwingConstants.CENTER);
private JButton startButton = new JButton("Start");
private JButton stopButton = new JButton("Stop");
private JButton resetButton = new JButton("Reset");
private Timer refreshRate = new Timer(1, new ActionListen());
private int milliseconds, seconds, minutes, hours;
public StopWatchPanel(){
// user panel settings
userPanel.setPreferredSize(new Dimension(400, 100));
userPanel.setBackground(new Color(200, 200, 255));
// add listeners
startButton.addActionListener(new ActionListen());
stopButton.addActionListener(new ActionListen());
resetButton.addActionListener(new ActionListen());
// component setting
timeDisplay.setPreferredSize(new Dimension(400, 50));
// add components
userPanel.add(timeDisplay);
userPanel.add(startButton);
userPanel.add(stopButton);
userPanel.add(resetButton);
// add to main frame
add(userPanel);
}
private int milsec(){
if(milliseconds > 1000){
milliseconds = 0;
seconds++;
}
return milliseconds++;
}
private int sec(){
if(seconds > 59){
seconds = 0;
minutes++;
}
return seconds;
}
private int min(){
if(minutes > 59){
minutes = 0;
hours++;
}
return minutes;
}
private int hrs(){
if(hours > 23){
hours = 0;
}
return hours;
}
private class ActionListen implements ActionListener{
public void actionPerformed(ActionEvent e){
if(e.getSource().equals(startButton)){
refreshRate.start();
}
else if(e.getSource().equals(refreshRate)){
timeDisplay.setText(sec()+":"+milsec());
}
else if(e.getSource().equals(stopButton)){
refreshRate.stop();
}
else if(e.getSource().equals(resetButton)){
timeDisplay.setText("00:00:00");
milliseconds = 0;
seconds = 0;
minutes = 0;
hours = 0;
}
}
}
}
it works if i set the delay on the timer to 1000ms therefore showing seconds (if i exclude the milsec method) but i want to display milliseconds as well. So I put delay to 1ms so that after each 1ms, it will setText to current count of second (sec method).
But for some reason this doesnt work because the numbers add up too slowly considering that 1000 milliseconds = 1 second.
What am i doing wrong?
The delay of 1 ms means that the timer will generate events every 1 millisecond. The events are then dispatched to the event dispatch thread, which introduces a delay (since there's other code also running on the event dispatch thread), so the actual interval between the calls to actionPerformed() is likely to be larger than 1 ms.
If you need your clock to be precise, you need to measure the exact elapsed time between calls (using, for example, the System.currentTimeMillis() method), rather than increment the time by 1 ms on every call.
You can improve the timeliness of the output by varying the millisecond interval. Instead of 1ms, you can try 10, 20 or 50ms interval. The code below uses 100ms.
package StopWatch;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class StopWatchPanel extends JPanel {
private JPanel userPanel = new JPanel();
private JLabel timeDisplay = new JLabel("00:00:00", SwingConstants.CENTER);
private JButton startButton = new JButton("Start");
private JButton stopButton = new JButton("Stop");
private JButton resetButton = new JButton("Reset");
private Timer refreshRate = new Timer(100, new ActionListen());
private int milliseconds, seconds, minutes, hours;
public StopWatchPanel(){
// user panel settings
userPanel.setPreferredSize(new Dimension(400, 100));
userPanel.setBackground(new Color(200, 200, 255));
// add listeners
startButton.addActionListener(new ActionListen());
stopButton.addActionListener(new ActionListen());
resetButton.addActionListener(new ActionListen());
// component setting
timeDisplay.setPreferredSize(new Dimension(400, 50));
// add components
userPanel.add(timeDisplay);
userPanel.add(startButton);
userPanel.add(stopButton);
userPanel.add(resetButton);
// add to main frame
add(userPanel);
}
private int milsec(){
milliseconds+=100;
if(milliseconds >= 1000){
milliseconds = 0;
seconds++;
}
return milliseconds;
}
private int sec(){
if(seconds > 59){
seconds = 0;
minutes++;
}
return seconds;
}
private int min(){
if(minutes > 59){
minutes = 0;
hours++;
}
return minutes;
}
private int hrs(){
if(hours > 23){
hours = 0;
}
return hours;
}
private class ActionListen implements ActionListener{
public void actionPerformed(ActionEvent e){
if(e.getSource().equals(startButton)){
refreshRate.start();
}
else if(e.getSource().equals(refreshRate)){
int tms = milsec();
int tsec = sec();
int tmin = min();
int thr = hrs();
timeDisplay.setText(thr + ":" +tmin+ ":" +tsec+ "." +tms/100);
}
else if(e.getSource().equals(stopButton)){
refreshRate.stop();
}
else if(e.getSource().equals(resetButton)){
timeDisplay.setText("00:00:00");
milliseconds = 0;
seconds = 0;
minutes = 0;
hours = 0;
}
}
}
}
I believe the original code was primarily made for demo purposes and does not concern too much on the accurateness of the time. If you want a more realtime output, you will need to try a different approach. In such case you may use the System.currentTimeMillis() as pointed out by the other answer.
My program seem to be using 20% of the CPU and around 1GB of RAM. I think its because I am looping the date. I am trying to make a clock appear on my JFrame (hours, mins and seconds always updating). My question is, how can I make my program less hungry for power?
Here's my code:
while(true){
Date date = new Date();
time.setText(date.getHours() + " hours " + date.getMinutes()
+ " minutes " + date.getSeconds() + " seconds!");
}
How can I make my program less hungry for power? Make your thread sleep for a while. I assumed the code #Cj1m given is run in a newly started thread.
See java.lang.Thread.sleep(long)
while(true){
SwingUtilities.invokeLater(new Runnable(){ // make sure to run in EDT
#Override
public void run(){
Date date = new Date();
time.setText(date.getHours() + " hours " + date.getMinutes()
+ " minutes " + date.getSeconds() + " seconds!");
}
});
try {
Thread.sleep(1000); // Sleep for 1000 milliseconds.
// Give a shorter interval if you like.
} catch(InterruptedException e) { // Who interrupted my dream?
e.printStackTrace();
}
}
Or use Swing Timer as others described.
Avoid this, use SwingTimer, swing timer not need any loop.
Here a full example:
import java.util.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class DateAndTimer extends JFrame {
private javax.swing.Timer timer;
private JLabel label ;
private Date ;
public DateAndTimer(){
this.timer = new javax.swing.Timer(1000,getActionTimer());
date = new Date();
label = new JLabel();
add(label,BorderLayout.PAGE_START);
timer.start();
setDefaultCloseOperation(3);
setVisible(true);
pack();
}
public ActionListener getActionTimer(){
ActionListener action = new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
label.setText(date.getHours() + " hours " + date.getMinutes()
+ " minutes " + date.getSeconds() + " seconds!");
}
};
return action;
}
public static void main(String...args){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
new DateAndTimer();
}
});
}
}
Don't loop. Whatever the application, the above infinite loop will place a constant demand on resources.
In this case, it appears you are using Swing. This is even worse for Swing applications. Infinite loops prevent UI updates.
Use a Swing Timer instead and set an period interval large enough that will allow updates to be observed and will demand less overhead from the CPU. 1000 milliseconds should do.
public class TimerDemo {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Timer Demo");
final JLabel timeLabel =
new JLabel("-----------------------------------------------");
Timer timer = new Timer(1000, new ActionListener() {
SimpleDateFormat format = new SimpleDateFormat("HH' hours 'mm' minutes 'ss' seconds'");
#Override
public void actionPerformed(ActionEvent e) {
Date date = new Date();
timeLabel.setText(format.format(date));
}
});
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(timeLabel);
frame.setVisible(true);
frame.pack();
timer.start();
}
});
}
}
Infinite loop with tiny load (setting date) would obviously take huge CPU, introducing sleep will lessen CPU usage:
while(true){
Date date = new Date();
time.setText(date.getHours() + " hours " + date.getMinutes()
+ " minutes " + date.getSeconds() + " seconds!");
Thread.sleep(1000);//1second update of clock
}
I'm wanting to create a stopwatch so to speak in order to score my game. Lets say I have a variable: int sec = 0. When the game starts I want a g.drawString to draw the time to the applet. So for example each second, sec will increment by 1.
How do I go about making it g.drawString(Integer.toString(sec), 40, 400) increment by 1 and draw each second?
Thanks.
EDIT:
I've figured out how to increment it and print it to the screen by using ActionListener and putting g.drawString in there but it prints ontop of each other. If I put g.drawString into the paint method and only increment sec by 1 in the ActionListener there is a a flicker. Should I use Double Buffering? If so how do I go about doing this?
import java.awt.event.*;
import javax.swing.*;
public class StopWatch extends JLabel
implements MouseListener, ActionListener {
private long startTime; // Start time of stopwatch.
// (Time is measured in milliseconds.)
private boolean running; // True when the stopwatch is running.
private Timer timer; // A timer that will generate events
// while the stopwatch is running
public StopWatch() {
// Constructor.
super(" Click to start timer. ", JLabel.CENTER);
addMouseListener(this);
}
public void actionPerformed(ActionEvent evt) {
// This will be called when an event from the
// timer is received. It just sets the stopwatch
// to show the amount of time that it has been running.
// Time is rounded down to the nearest second.
long time = (System.currentTimeMillis() - startTime) / 1000;
setText("Running: " + time + " seconds");
}
public void mousePressed(MouseEvent evt) {
// React when user presses the mouse by
// starting or stopping the stopwatch. Also start
// or stop the timer.
if (running == false) {
// Record the time and start the stopwatch.
running = true;
startTime = evt.getWhen(); // Time when mouse was clicked.
setText("Running: 0 seconds");
if (timer == null) {
timer = new Timer(100,this);
timer.start();
}
else
timer.restart();
}
else {
// Stop the stopwatch. Compute the elapsed time since the
// stopwatch was started and display it.
timer.stop();
running = false;
long endTime = evt.getWhen();
double seconds = (endTime - startTime) / 1000.0;
setText("Time: " + seconds + " sec.");
}
}
public void mouseReleased(MouseEvent evt) { }
public void mouseClicked(MouseEvent evt) { }
public void mouseEntered(MouseEvent evt) { }
public void mouseExited(MouseEvent evt) { }
} // end StopWatchRunner
A small applet to test the component:
/*
A trivial applet that tests the StopWatchRunner component.
The applet just creates and shows a StopWatchRunner.
*/
import java.awt.*;
import javax.swing.*;
public class Test1 extends JApplet {
public void init() {
StopWatch watch = new StopWatch();
watch.setFont( new Font("SansSerif", Font.BOLD, 24) );
watch.setBackground(Color.white);
watch.setForeground( new Color(180,0,0) );
watch.setOpaque(true);
getContentPane().add(watch, BorderLayout.CENTER);
}
}