Reputation: 689
I am trying to implement a Java Stop Watch in which I have used a Thread to continuously moniter time elapsed. But i have encountered a problem in my code. My Java Code is -
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Calendar;
import javax.swing.*;
public class Prac2_StopWatch implements ActionListener{
JFrame Time;
JLabel TimeLabel;
JButton StartStop;
JButton Reset;
StopWatch stThread;
String prefix="<html><h1><body>";
String suffix="</h1></body></html>";
StopWatch myControl;
Prac2_StopWatch()
{
Time = new JFrame();
Time.setSize(275,275);
Time.setTitle("Stop Watch");
Time.setLayout(new GridBagLayout());
GridBagConstraints gridConst = new GridBagConstraints();
TimeLabel = new JLabel();
TimeLabel.setText(prefix+"0:0:0"+suffix);
myControl = new StopWatch(TimeLabel);
myControl.start();
StartStop = new JButton();
Reset = new JButton();
StartStop.setActionCommand("Start");
StartStop.setText("Start");
Reset.setText("Reset");
StartStop.addActionListener(this);
Reset.addActionListener(this);
gridConst.gridx=0;
gridConst.gridy=0;
Time.add(StartStop,gridConst);
gridConst.gridx=1;
gridConst.gridy=0;
Time.add(Reset,gridConst);
gridConst.gridx=0;
gridConst.gridy=1;
Time.add(TimeLabel,gridConst);
Time.setVisible(true);
}
public void actionPerformed(ActionEvent evt)
{
if(evt.getActionCommand()=="Start")
{
StartStop.setActionCommand("Stop");
StartStop.setText("Stop");
if(myControl.curMil==-1)
myControl.curMil=Calendar.getInstance().getTimeInMillis();
else
myControl.curMil=Calendar.getInstance().getTimeInMillis()- myControl.diff;
myControl.count=true;
}
else if(evt.getActionCommand()=="Stop")
{
StartStop.setActionCommand("Start");
StartStop.setText("Start");
myControl.count=false;
}
else if(evt.getActionCommand()=="Reset")
{
StartStop.setActionCommand("Start");
StartStop.setText("Start");
myControl.count=false;
myControl.curMil=-1;
TimeLabel.setText(prefix+"0:0:0"+suffix);
}
}
public static void main(String args[])
{
SwingUtilities.invokeLater(new Runnable(){
public void run()
{
new Prac2_StopWatch();
}
});
}
}
class StopWatch extends Thread
{
long curMil;
long diff;
JLabel TimeLabel;
boolean count;
String prefix="<html><body><h1>";
String suffix="</h1></body></html>";
StopWatch(JLabel TimeLabel)
{
this.TimeLabel = TimeLabel;
this.count=false;
this.curMil=-1;
}
public void run()
{
while(true){
System.out.print("Commenting this line will make stop watch useless");
if(count){
diff= Calendar.getInstance().getTimeInMillis() - curMil;
TimeLabel.setText(prefix+((diff/60000)%60)+" : "+((diff/1000)%60)+" : "+(diff%1000)+suffix);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
Now the problem is that in the run method of StopWatch class when I comment the statement -
System.out.println("Commenting this line will make stop watch useless");
in line 123.
My stop watch stops working i.e. the clock always displays "0:0:0" even if press 'Start', 'Stop' or 'Reset'. Moreover, replacing the line 123 with any other statement other than 'System.out.println()' has the same issue.
Upvotes: 0
Views: 92
Reputation: 29282
Move
if(count){
diff= Calendar.getInstance().getTimeInMillis() - curMil;
TimeLabel.setText(prefix+((diff/60000)%60)+":"+((diff/1000)%60)+" : "+(diff%1000)+suffix);
}
inside try-catch
block.
Side notes:
Your program doesn't stops when JFrame
window is closed. Use
Time.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Proper way to compare Strings in java is
if("string1".equals("string2")) {
//do stuff
}
Upvotes: 3
Reputation: 285405
Two main problems that I see:
setText(...)
from your background thread, something that you shouldn't be doing, and that with the tight loop can completely freeze your Swing GUI. volatile
so that its state will be the same in all threads.If you need to make Swing state changes from within a background thread, use a SwingWorker and make the changes using the publish/process pair.
Also, don't compare Strings using ==
or !=
. Use the equals(...)
or the equalsIgnoreCase(...)
method instead. Understand that ==
checks if the two object references are the same which is not what you're interested in. The methods on the other hand check if the two Strings have the same characters in the same order, and that's what matters here. So instead of
else if (evt.getActionCommand() == "Stop") {
do
else if (evt.getActionCommand().equalsIgnoreCase("Stop")) {
e.g.,
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Calendar;
import javax.swing.*;
public class Prac2_StopWatch implements ActionListener {
JFrame time;
JLabel timeLabel;
JButton startStop;
JButton reset;
StopWatch stThread;
String prefix = "<html><h1><body>";
String suffix = "</h1></body></html>";
StopWatch myControl;
Prac2_StopWatch() {
time = new JFrame();
time.setSize(275, 275);
time.setTitle("Stop Watch");
time.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
time.setLayout(new GridBagLayout());
GridBagConstraints gridConst = new GridBagConstraints();
timeLabel = new JLabel();
timeLabel.setText(prefix + "00:00:000" + suffix);
myControl = new StopWatch(timeLabel);
new Thread(myControl).start(); // StopWatch should implement Runnable
// myControl.start();
startStop = new JButton();
reset = new JButton();
startStop.setActionCommand("Start");
startStop.setText("Start");
reset.setText("Reset");
startStop.addActionListener(this);
reset.addActionListener(this);
gridConst.gridx = 0;
gridConst.gridy = 0;
time.add(startStop, gridConst);
gridConst.gridx = 1;
gridConst.gridy = 0;
time.add(reset, gridConst);
gridConst.gridx = 0;
gridConst.gridy = 1;
time.add(timeLabel, gridConst);
time.setVisible(true);
}
public void actionPerformed(ActionEvent evt) {
if (evt.getActionCommand().equalsIgnoreCase("Start")) {
startStop.setActionCommand("Stop");
startStop.setText("Stop");
if (myControl.getCurMil() == -1)
myControl.setCurMil(Calendar.getInstance().getTimeInMillis());
else
myControl.setCurMil(Calendar.getInstance().getTimeInMillis() - myControl.getDiff());
myControl.count = true;
} else if (evt.getActionCommand().equalsIgnoreCase("Stop")) {
startStop.setActionCommand("Start");
startStop.setText("Start");
myControl.count = false;
} else if (evt.getActionCommand().equalsIgnoreCase("Reset")) {
startStop.setActionCommand("Start");
startStop.setText("Start");
myControl.count = false;
myControl.setCurMil(-1);
timeLabel.setText(prefix + "00:00:000" + suffix);
}
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Prac2_StopWatch();
}
});
}
}
class StopWatch implements Runnable {
private long curMil;
private long diff;
private JLabel timeLabel;
// *************** this is key ****************
volatile boolean count; // !! *********************
String prefix = "<html><body><h1>";
String suffix = "</h1></body></html>";
StopWatch(JLabel TimeLabel) {
this.timeLabel = TimeLabel;
this.count = false;
this.curMil = -1;
}
public long getCurMil() {
return curMil;
}
public void setCurMil(long curMil) {
this.curMil = curMil;
}
public long getDiff() {
return diff;
}
public void run() {
while (true) {
// System.out.println("Commenting this line will make stop watch useless");
if (count) {
diff = Calendar.getInstance().getTimeInMillis() - curMil;
// make Swing changes **on** the event thread only
SwingUtilities.invokeLater(() -> {
int mSec = (int) (diff % 1000);
int sec = (int) ((diff / 1000) % 60);
int min = (int) ((diff / (60 * 1000)) % 60);
String text = String.format("%s%02d:%02d:%03d%s", prefix, min, sec, mSec, suffix);
timeLabel.setText(text);
});
try {
Thread.sleep(1); // ** actually 1 is OK **
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
Upvotes: 3