1

I've tried to write a litte java-program that should work as a timer.

The problem is (probably) that I need to use Thread.sleep(), which throws InterruptedException. So if I start the program, I am able to enter the seconds an minutes, but when pressing "OK", nothing happens (it also doesn't show any warnings or errors in the terminal).

Here's the code:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import javax.swing.text.NumberFormatter;

import static javax.swing.WindowConstants.EXIT_ON_CLOSE;

public class Timer {

   public static void main(String[] args) throws InterruptedException {

       SwingUtilities.invokeLater(Timer::new);
   }

   private JButton button;
   private JTextField minutes;
   private JTextField seconds;

   Timer() {
       JFrame frame = new JFrame();
       frame.setSize(300, 200);
       frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
       JPanel panel = new JPanel(new BorderLayout());
       JPanel subpanel1 = new JPanel(new GridLayout(1, 2));

       /* 
        * The following lines ensure that the user can 
        * only enter numbers.
       */

       NumberFormat format = NumberFormat.getInstance();
       NumberFormatter formatter = new NumberFormatter(format);
       formatter.setValueClass(Integer.class);
       formatter.setMinimum(0);
       formatter.setMaximum(Integer.MAX_VALUE);
       formatter.setAllowsInvalid(false);
       formatter.setCommitsOnValidEdit(true);

       minutes = new JFormattedTextField(formatter);
       seconds = new JFormattedTextField(formatter);
       minutes.setText("0");
       seconds.setText("0");

       JPanel subpanel2 = new JPanel();

       /*
        * When the user presses the OK-button, the program will
        * start to count down.
       */

       button = new JButton("OK");
       button.addActionListener(new ActionListener() {
           @Override
           public void actionPerformed(ActionEvent actionEvent) {
               try {
                   timer();
               }
               catch(InterruptedException e) {

               }
           }
       });

       JButton button2 = new JButton("Reset");
       button2.addActionListener(new ActionListener() {
           @Override
           public void actionPerformed(ActionEvent actionEvent) {
               minutes.setText("0");
               seconds.setText("0");
               button.setEnabled(true);
           }
       });
       subpanel1.add(minutes);
       subpanel1.add(seconds);
       subpanel2.add(button);
       subpanel2.add(button2);
       panel.add(subpanel1, BorderLayout.CENTER);
       panel.add(subpanel2, BorderLayout.PAGE_END);
       frame.add(panel);
       frame.setVisible(true);
   }


   //Method for the timer
   void timer() throws InterruptedException {
       int min = Integer.valueOf(minutes.getText());
       int sec = Integer.valueOf(seconds.getText());
       button.setEnabled(false);


       while(min > 0 || sec > 0) {
           Thread.sleep(1000);
           if(sec >= 1) {
               sec = sec - 1;
               seconds.setText(String.valueOf(sec));
           }
           else if(sec == 0 || min > 0) {
               sec = 59;
               min = min - 1;
               seconds.setText(String.valueOf(sec));
               minutes.setText(String.valueOf(min));
           }
       }
   }
}

What can I do to make it work?


I know replaced Thread.sleep() with the following:

Timer timer = new Timer(delay, taskPerformer);
timer.setRepeats( false );
timer.start();

Now an error occurs: The constructor cannot applied to given types (it wants no arguments). What did I do wrong? (The variables delay and taskPerformer are defined, and I did not import java.util.Timer)

  • 2
    You should be using a Swing Timer - since your GUI should be running from the EDT, putting your only thread to sleep would freeze your program. – sleepToken Mar 11 '20 at 10:58
  • 1
    `Thread.sleep` is not permiited in Swing. Use [Timer](https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html) instead. Please also read about [concurrency in swing](https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html). – Sergiy Medvynskyy Mar 11 '20 at 11:00
  • I just edited my question. What did I do wrong now? –  Mar 11 '20 at 11:30
  • 1
    @chrysaetos99 probably you should rename your class. To avoid name colission (name of your class is also `Timer`). – Sergiy Medvynskyy Mar 11 '20 at 11:57
  • 1
    Your basic code is still wrong. A Timer replaces looping code. There is no need for a `Thread.sleep(…)`. The Timer is only created once. A Timer will only fire an event at your specified time interval. In the ActionListener you then do something. So in your case in the ActionListener for the button you would set a variable to contain the total seconds for the minutes/seconds you want the Timer to run and update the JLabels to contain the minutes and seconds left.. You would then set the Timer to fire in 1000ms (i.e. one second) – camickr Mar 11 '20 at 14:22
  • 1
    In the ActionListener for the Timer you would then subtract 1 from the total seconds. Then update the minutes/seconds labels. Then if the total seconds is equal to 0, you would stop the timer. See: https://stackoverflow.com/questions/7816585/program-freezes-during-thread-sleep-and-with-timer/7816604#7816604 for a basic example to get you started. – camickr Mar 11 '20 at 14:23

2 Answers2

1

The following mre shows how you can use javax.swing.Timer to get the desired countdown :

import static javax.swing.WindowConstants.EXIT_ON_CLOSE;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.text.NumberFormat;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.text.NumberFormatter;

public class MyTimer {

    public static void main(String[] args) throws InterruptedException {
        SwingUtilities.invokeLater(MyTimer::new);
    }

    private final JButton startButton, resetButton;
    private final JTextField minutes;
    private final JTextField seconds;
    private static final int SECOND = 1000;

    private Timer timer;

    MyTimer() {
        JFrame frame = new JFrame();
        //frame.setSize(300, 200);
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);

        /*
         * The following lines ensure that the user can
         * only enter numbers.
         */
        NumberFormat format = NumberFormat.getInstance();
        NumberFormatter formatter = new NumberFormatter(format);
        formatter.setValueClass(Integer.class);
        formatter.setMinimum(0);
        formatter.setMaximum(Integer.MAX_VALUE);
        formatter.setAllowsInvalid(false);
        formatter.setCommitsOnValidEdit(true);

        minutes = new JFormattedTextField(formatter);
        seconds = new JFormattedTextField(formatter);
        minutes.setText("0");
        seconds.setText("0");

        /*
         * When the user presses the OK-button, the program will
         * start to count down.
         */
        startButton = new JButton("OK");
        startButton.addActionListener(actionEvent -> {
            timer();
        });

        resetButton = new JButton("Reset");
        resetButton.setEnabled(false);
        resetButton.addActionListener(actionEvent -> {
            reset();
        });

        JPanel subpanel1 = new JPanel(new GridLayout(1, 2));
        subpanel1.add(minutes);
        subpanel1.add(seconds);

        JPanel subpanel2 = new JPanel();
        subpanel2.add(startButton);
        subpanel2.add(resetButton);

        JPanel panel = new JPanel(new BorderLayout());
        panel.add(subpanel1, BorderLayout.CENTER);
        panel.add(subpanel2, BorderLayout.PAGE_END);
        frame.add(panel);
        frame.pack();
        frame.setVisible(true);
    }

    void timer() {

        startButton.setEnabled(false);

        timer = new Timer(SECOND, e->{

            final int min = Integer.valueOf(minutes.getText());
            final int sec = Integer.valueOf(seconds.getText());
            if(sec == 0 && min == 0){
                reset();
            }else if(sec >= 1) {
                seconds.setText(String.valueOf(sec-1));
            }
            else if(sec == 0 || min > 0) {
                seconds.setText(String.valueOf(59));
                minutes.setText(String.valueOf(min-1));
            }
        });

        timer.setRepeats(true);
        timer.start();
        resetButton.setEnabled(true);
    }

    private void reset() {

        if(timer != null) {
            timer.stop();
        }
        minutes.setText("0");
        seconds.setText("0");
        startButton.setEnabled(true);
    }
}
c0der
  • 18,467
  • 6
  • 33
  • 65
0

Since you need the timer only for showing hours, you could simply create a new thread to handle that, leaving the main thread to handle other stuff like user IO.

Varejator
  • 17
  • 7