1

I am trying to write the simples GUI countdown. I found in Internet some code but it is already too fancy for me. I am trying to keep it as simple as possible. So, I just want to have a window saying "You have 10 second left". The number of second should decrease every second from 10 to 0. I wrote a code. And I think I am close to the working solution. But I still missing something. Could you pleas help me to find out what is wrong? Here is my code:

import javax.swing.*;

public class Countdown {

    static JLabel label;

    // Method which defines the appearance of the window.   
    private static void showGUI() {
        JFrame frame = new JFrame("Simple Countdown");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JLabel label = new JLabel("Some Text");
        frame.add(label);
        frame.pack();
        frame.setVisible(true);
    }

    // Define a new thread in which the countdown is counting down.
    static Thread counter = new Thread() {
        public void run() {
            for (int i=10; i>0; i=i-1) {
                updateGUI(i,label);
                try {Thread.sleep(1000);} catch(InterruptedException e) {};
            }
        }
    };

    // A method which updates GUI (sets a new value of JLabel).
    private static void updateGUI(final int i, final JLabel label) {
        SwingUtilities.invokeLater(new Runnable(i,label) {

            public Runnable(int i, JLabel label) {
                this.i = i;
                this.label = label;
            }

            public void run() {
                label.setText("You have " + i + " seconds.");
            }

        });
    }

    // The main method (entry point).
    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                showGUI();
                //counter.start();
            }
        });
        //counter.start();
    }

}

And I have several concrete question about this code:

  1. Where should I place the counter.start();? (In my code I put it on 2 places. Which one is correct?)

  2. Why compiler complains about the constructor for Runnable? It says that I have an invalid method declaration and I need to specify the returned type.

ADDED: I made the suggested corrections. And then I execute the code and get:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at Worker.run(Worker.java:12)

In the Worker.java in the line 12 I have: label.setText("You have " + i + " seconds.");.

Roman
  • 124,451
  • 167
  • 349
  • 456
  • Just a comment: I wouldn't rely on thread.wait() to wait exactly for 1000 ms. Look at the time remaining and generate the seconds from that dynamically. Store the time when you start the loop, and keep querying the time when the sleep interval expires. – Chris Dennett Mar 19 '10 at 17:57
  • JLabel label = new JLabel("Some Text"); change this to label = new JLabel("Some Text"); You weren't setting the field before which was being used later on.. – Chris Dennett Mar 19 '10 at 18:11
  • Chris Dennett, your suggestion works. Now I have a working application. Thanks a lot! However, I did not get the very last problem. If I use "JLabel" before the "label", I make the "label" a local variable of the method? – Roman Mar 19 '10 at 18:16
  • There is the [`javax.swing.Timer`](https://docs.oracle.com/javase/8/docs/api/javax/swing/Timer.html) class which fits your use case very well. It uses one thread for all timers so you do not have to worry about too many threads and it fires the events on the EDT. – Marcono1234 Jul 16 '19 at 08:21

2 Answers2

2

Call counter.start() inside the runnable:

// The main method (entry point).
public static void main(String[] args) {
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            showGUI();
            counter.start();
        }
    });
}

You really want a specific order of invocation, if you place it outside the thread then the counter will start even before the GUI exists and it will fail on you.

For the second question:

// A method which updates GUI (sets a new value of JLabel).
private static void updateGUI(final int i, final JLabel label) {
    SwingUtilities.invokeLater(new Worker(i, label));
}

Here is the worker:

import javax.swing.JLabel;

public class Worker implements Runnable{
    private int i;
    private JLabel label;
    public Worker(int i, JLabel label) {
        this.i = i;
        this.label = label;
    }

    public void run() {
        label.setText("You have " + i + " seconds.");
    }
}

And your main now:

// The main method (entry point).
public static void main(String[] args) {
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            Countdown.showGUI();
            counter.start();
        }
    });
}

UPDATE:
Or if you still want to use the anonymous pattern then:

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class Countdown {

    static JLabel label;

    // Method which defines the appearance of the window.   
    public static void showGUI() {
        JFrame frame = new JFrame("Simple Countdown");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        label = new JLabel("Some Text");
        frame.add(label);
        frame.pack();
        frame.setVisible(true);
    }

    // Define a new thread in which the countdown is counting down.
    public static Thread counter = new Thread() {
        public void run() {
            for (int i=10; i>0; i=i-1) {
                updateGUI(i,label);
                try {Thread.sleep(1000);} catch(InterruptedException e) {};
            }
        }
    };

    // A method which updates GUI (sets a new value of JLabel).
    private static void updateGUI(final int i, final JLabel label) {
        SwingUtilities.invokeLater( 
            new Runnable() {
                public void run() {
                    label.setText("You have " + i + " seconds.");
                }
            }
        );
    }
}
Kiril
  • 39,672
  • 31
  • 167
  • 226
1

The anonymous example in the answer works perfectly.

As for the 1st answer, the counter work, but no GUI get displayed, and at each second the below exception appears

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
        at com.aramco.ecc.licenseoptimizer.gui.Worker.run(Worker.java:23)
        at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Yahya
  • 11
  • 1