Reputation: 369
I am trying to make a simple java application that counts time, with the ability to stop and start the timer. However, the label won't update, and when I press start, it freezes.
Could you help me figure out what the problem is?
package random;
import javax.swing.JFrame;
public class Timer {
boolean shouldCount=false;
int int_sec=0;
int int_min=0;
int int_mil=0;
public static void main(String[] args) {
TimeFrame t = new TimeFrame();
JFrame f = new JFrame("Timer");
f.setSize(300,200);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationRelativeTo(null);
f.getContentPane().add(t);
f.setVisible(true);
}
public void count(){
TimeFrame t = new TimeFrame();
if(shouldCount){
long now = System.currentTimeMillis();
while(true){
if(System.currentTimeMillis()-now>=100){
now=System.currentTimeMillis();
String sec = Integer.toString(int_sec);
String min = Integer.toString(int_min);
String mil = Integer.toString(int_mil);
t.update(sec,int_sec,min,mil,int_mil);
int_mil++;
if(int_mil>9){
int_mil=0;
int_sec++;
if(int_sec>=60){
int_sec=1;
int_min++;
}
}
}
}
}
}
}
And here is TimeFrame.java
package random;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class TimeFrame extends JPanel{
JLabel time = new JLabel("Time goes here", JLabel.CENTER);
Timer t = new Timer();
JButton pause = new JButton ("Pause");
JButton start = new JButton ("Start");
public TimeFrame(){
start.addActionListener(new starts());
pause.addActionListener(new starts());
add(time);
add(start);
add(pause);
}
public void update(String sec,int s, String min,String mil,int m){
if (s<=10){
sec="0"+sec;
}
System.out.println(min+":"+sec+","+mil);
time.setText(min+":"+sec+","+mil);
}
public class starts implements ActionListener{
public void actionPerformed(ActionEvent event){
if(event.getSource() == start){
t.shouldCount=true;
}else{
t.shouldCount=false;
}
t.count();
}
}
}
Upvotes: 3
Views: 17944
Reputation: 2639
The problem is that you have only one thread in your application. You should have at least two : one for the UI that will update the text and one for the computation of the time.
If you have only one thread it hangs in the while(true) loop and Swing never get to update the view.
I refactored your code using two threads :
One counting until the end of time and updating the fields to keep time in memory
Another one that uses a java.util.Timer#scheduleAtFixedRate() method wich is invoqued every 100 millisec to update the view.
Timer.java
(avoid naming classes like ones in the Java API)
public class Timer {
boolean shouldCount=false;
int int_sec=0;
int int_min=0;
int int_mil=0;
public Timer() {
}
public static void main(String[] args) {
TimeFrame t = new TimeFrame();
JFrame f = new JFrame("Timer");
f.setSize(300,200);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationRelativeTo(null);
f.getContentPane().add(new TimeFrame());
f.setVisible(true);
}
public void count(){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
long now = System.currentTimeMillis();
while(true){
if(shouldCount){
if(System.currentTimeMillis()-now>=100){
now=System.currentTimeMillis();
int_mil++;
if(int_mil>9){
int_mil=0;
int_sec++;
if(int_sec>=60){
int_sec=1;
int_min++;
}
}
}
}
}
}
});
thread.start();
}
}
}
TimeFrame
(I would rather call it TimePanel
since it extends JPanel
)
public class TimeFrame extends JPanel{
JLabel time;
Timer t ;
JButton pause ;
JButton start ;
public TimeFrame(){
t= new Timer(this);
time = new JLabel("Time goes here", JLabel.CENTER);
pause = new JButton ("Pause");
start = new JButton ("Start");
start.addActionListener(new starts());
pause.addActionListener(new starts());
add(time);
add(start);
add(pause);
java.util.Timer updateTimer= new java.util.Timer();
updateTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
t.update(int_sec,int_min,int_mil);
}
}, 0, 100);
}
public void update(int s, int minute,int m){
String sec = Integer.toString(s);
String min = Integer.toString(minute);
String mil = Integer.toString(m);
if (s<=10){
sec="0"+sec;
}
System.out.println(min+":"+sec+","+mil);
time.setText(min+":"+sec+","+mil);
}
public class starts implements ActionListener{
boolean firstTime=true;
public void actionPerformed(ActionEvent event){
if (firstTime){
t.count();
firstTime = false;
}
if(event.getSource() == start){
t.shouldCount=true;
}else{
t.shouldCount=false;
}
}
}
}
Upvotes: 3
Reputation: 11440
The problem is your while loop is tied to your interface thread. When you click the start button it calls Timer.count() and then goes into an infinite loop causing the interface to be stuck and never update.
The assumptions that using the java.util.Timer class is better may be over estimating that classes functionality for this specific problem. It does not contain a pause method and you have to recreate the timer when you want to pause it, causing some possible difficult challenges for adding up time.
What I would do is make your timer implement the runnable interface and use a thread to keep tabs on your current time. Heres the changes I would make
Notice I made your fields private. It is proper practice to make your fields private (if they should be) and use getters and setters to grant access to them. eg: getCurrentTime()
Timer.java:
package random;
import javax.swing.JFrame;
public class Timer implements Runnable {
private Thread runThread;
private boolean running = false;
private boolean paused = false;
private TimeFrame timeFrame;
private long summedTime = 0;
public Timer(TimeFrame timeFrame) {
this.timeFrame = timeFrame;
}
public static void main(String[] args) {
TimeFrame t = new TimeFrame();
JFrame f = new JFrame("Timer");
f.setSize(300,200);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationRelativeTo(null);
f.getContentPane().add(t);
f.setVisible(true);
}
public void startTimer() {
running = true;
paused = false;
// start the thread up
runThread = new Thread(this);
runThread.start();
}
public void pauseTimer() {
// just pause it
paused = true;
}
public void stopTimer() {
// completely stop the timer
running = false;
paused = false;
}
@Override
public void run() {
long startTime = System.currentTimeMillis();
// keep showing the difference in time until we are either paused or not running anymore
while(running && !paused) {
timeFrame.update(summedTime + (System.currentTimeMillis() - startTime));
}
// if we just want to pause the timer dont throw away the change in time, instead store it
if(paused)
summedTime += System.currentTimeMillis() - startTime;
else
summedTime = 0;
}
}
TimeFrame.java:
package random;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class TimeFrame extends JPanel{
private JLabel time = new JLabel("Time goes here", JLabel.CENTER);
private Timer timer;
private JButton pause = new JButton ("Pause");
private JButton start = new JButton ("Start");
public TimeFrame(){
timer = new Timer(this);
start.addActionListener(new starts());
pause.addActionListener(new starts());
add(time);
add(start);
add(pause);
}
public void update(long dT){
// convert milliseconds into other forms
time.setText(String.valueOf((dT/6000)%1000)+":"+String.valueOf((dT/1000)%1000)+","+String.valueOf((dT)%1000));
}
public class starts implements ActionListener{
public void actionPerformed(ActionEvent event){
if(event.getSource() == start){
timer.startTimer();
}else{
timer.pauseTimer();
}
}
}
}
Upvotes: 1