Search code examples
javaswingtimerjlabel

How do you change label text in java after a timer ticks a second passed?


I have a timer with a delay of 5 seconds. I am trying to change the label named lblTimer after a second passes to act as a countdown. I am having issues with it as currently it only works at 5 seconds. Do you have any suggestionsuggestions?

protected void Addition() {
    //addition function 

    final int delay = 5000; //milliseconds

    ActionListener taskPerformer = new ActionListener() {
        @SuppressWarnings("unused")
        public void actionPerformed(ActionEvent evt) {
            //...Perform a task...
            frame.getContentPane().setBackground(Color.red);
        }
    };

    new Timer(delay, taskPerformer).start();
    Random RandomNumber = new Random();
    int number1 = RandomNumber.nextInt(12);
    int number2 = RandomNumber.nextInt(12);
    int number3 = RandomNumber.nextInt(12);
    lblAnswer.setText("");
    lblFirstNumber.setText(""+ number1);
    lblfunction1.setText("+");
    lblsecondNumber.setText(""+number2);
    lblfunction2.setText("+");
    lblthirdnumber.setText(""+number3);
    lblequals.setText("=");
    answer = number1+number2+number3;
    if(delay <= 1000){
        lblTimer.setText("1"); 
    }    
    else if(delay == 2000){
        lblTimer.setText("2"); 
    }    
    else if(delay == 3000){
        lblTimer.setText("3"); 
    } 
    else if(delay == 4000){
        lblTimer.setText("4"); 
    }    
    else if (delay == 5000){
        lblTimer.setText("5"); 
    }
}

Solution

  • The answer to your question, that I assume is "why does this not work?", is that at no point do you recheck the elapsed time. The variable delay is always set at 5000, and never updated, also.

    The stupid-ass solution:

    lblTimer.setText("5");
    Thread.sleep(1000)
    lblTimer.setText("4");
    Thread.sleep(1000)
    lblTimer.setText("3");
    Thread.sleep(1000)
    lblTimer.setText("2");
    Thread.sleep(1000)
    lblTimer.setText("1");
    Thread.sleep(1000)
    lblTimer.setText("0");
    

    Don't really do this, unless you need to satisfy your sick fetishes.

    The four-liner

    The same as above. Don't do this.

    for (int i = secondsToWait; i >= 0; i--) {
        lblTimer.setText(i + "");
        Thread.sleep(1000);
    }
    

    The acceptable solution:

    Use a Timer to schedule a task to be executed after a given period of time. You can use timers to also fire the same task multiple times at a given interval.

        Timer timer = new Timer();
    
        int secondsToWait = 5;
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                secondsToWait--;
                lblTimer.setText(secondsToWait + "");
                if (secondsToWait == 0) {
                    timer.cancel();
                    timer.purge();
                }
            }
        };
    
        lblTimer.setText(secondsToWait + "");
        timer.scheduleAtFixedRate(task, 1000, 1000);
    

    The best solution:

    Instead of a timer, use a ScheduledExecutorService. This is better because of the way ScheduledExecutorService works with threads as opposed to Timer. Google it.

        ScheduledExecutorService exec = Executors.newScheduledThreadPool(1);
    
        int secondsToWait = 5;
        Runnable task = new Runnable() {
            @Override
            public void run() {
                secondsToWait--;
                lblTimer.setText(secondsToWait + "");
                if (secondsToWait == 0) {
                    exec.shutdown();
                }
            }
        };
    
        lblTimer.setText(secondsToWait + "");
        exec.scheduleAtFixedRate(task, 1, 1, TimeUnit.SECONDS);
    

    EDIT: As Stefan pointed out, for swing GUI applications a swing Timer would be the best pick.