I am trying to create a digital clock using a Thread as this seems to me the logical way one would do it.
I am not sure if I am going about it the right way but what I had in mind is to create the initial current System time using the JFrame constructor and display it as text using a label. In the constructor I then create the thread object with which to update the time.
Struggling a bit and was hoping for some advice as to how to do it right.
setDefaultCloseOperation((JFrame.EXIT_ON_CLOSE));
setBounds(50, 50, 200, 200);
JPanel pane = new JPanel();
label = new JLabel();
//Font localTime = new Font("Lumina", Font.BOLD , 24);
pane.add(label);
add(pane);
sdf = new SimpleDateFormat("HH:mm:ss");
date = new Date();
s = sdf.format(date);
label.setText(s);
setVisible(true);
runner = new Thread(this);
while(runner == null)
{
runner = new Thread(this);
runner.start();
}
This is then my run() method to update the clock every second.
public void run()
{
while(true)
{
try
{
Thread.sleep(1000);
sdf = new SimpleDateFormat("HH:mm:ss");
date = new Date();
s = sdf.format(date);
label.setText(s);
}
catch(Exception e){}
}
Main method.
public static void main(String[] args)
{
new DigitalClock().setVisible(true);
}
The label state should be updated in the Event Dispatch Thread.
You need to add the following modification:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
label.setText(s);
}
});
instead of simply updating the label from the separate thread.
It's worth to have a look at the simple description of The Swing GUI Freezing Problem and it's simple solution.
What do you want to improve? It looks ok, while(runner == null) not necessary, you're initialising runner just above.
Check this class http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Timer.html
scheduleAtFixedRate(TimerTask task, long delay, long period) is probably what you need.
Related
I found an example of how to do a repeating task in java. Now I want that the label's text changes every second. How do I do that?
I get the error: non-static method repeatingTask() cannot be referenced from a static context
Somehow the JLabel is not static but public static void main is of course static...
public class whathappens {
StartGUI startGUI = new StartGUI();
public void repeatingTask(){
getJLabel1().setText("Running: "+ new java.util.Date());
}
public static void main(String[] args) {
StartGUI.main(args);
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
repeatingTask();
System.out.println("Running: " + new java.util.Date());
}
}, 0, 1000);
}
}
My idea was to call the method getJLabel1() from startGUI class to change the label within "whathappens" class
Your not clear about how you get your JLabel.
One way or another, your timer task must hold a reference to the JLabel. You can try something like that :
JLabel label = new JLabel();
Timer timer = new Timer(1000, e -> label.setText("Running: " + new Date());
timer.start();
This code uses javax.swing.Timer which is better than java.util.Timer in your context as if fires event within the https://en.wikipedia.org/wiki/Event_dispatching_thread. And it is in this thread that GUI code must be updated.
Unable To Update Time In Every Second
import java.awt.*;
import java.applet.*;
import java.util.*;
public class AppletClock extends Applet{
Calendar calendar;
Thread changeTime = null;
Thread changeBgColor = null;
String currentTime;
Color randomColor;
Font font = new Font("Arial",Font.BOLD+Font.ITALIC,80);
public void init(){
setBackground(Color.black);
setForeground(Color.WHITE);
changeTime = new Thread(new Runnable(){
public void run(){
for(;;){
calendar = Calendar.getInstance();/*When I Instantiate calendar object outside of for loop this code doesnot work and the time didnt gets updated every second Its because of this factory method or something else*/
currentTime = calendar.get(Calendar.HOUR) +":"+calendar.get(Calendar.MINUTE)+":"+calendar.get(Calendar.SECOND);
try{
Thread.sleep(1000);
}
catch(Exception e){
System.out.println(e);
}
repaint();
}
}
});
changeTime.start();
changeBgColor = new Thread(new Runnable(){
public void run(){
for(;;){
Random random = new Random();
int red = random.nextInt(255);
int green = random.nextInt(255);
int blue = random.nextInt(255);
randomColor = new Color(red,green,blue);
try{
Thread.sleep(1000);
}
catch(Exception e){
System.out.println(e);
}
repaint();
}
}
});
changeBgColor.start();
}
public void paint(Graphics g){
setBackground(randomColor);
g.setFont(font);
g.drawString(currentTime,80,120);
}
}
/*
<APPLET CODE = "AppletClock.class" WIDTH = 500 HEIGHT = 200></APPLET>
*/
When I Instantiate calendar object outside of for loop this code does not work and the time didn't gets updated every second.
Its because of this factory method or something else
When calendar instance gets created it sets the time with current time so if you create instance in 2013 and access the same instance without updating it in 2014 it will still hold the time at which it was created
if you want to update instance each time you can just set the current millisecond
calendar.setTime(System.currentTimeInMillis());
A Calendar represents some fixed time. So if you instantiate it once, outside of the loop, it will always represents the same time, unless you update it in the loop, using, for example
calendar.setTime(new Date());
or
calendar.setTimeInMillis(System.currentTimeMillis());
That said, given that you only need the calendar inside this loop, I don't see why you wouldn't keep this solution, and declare it as a local variable instead of declaring it as a field.
The time is set when you call calendar = Calendar.getInstance(); When you do that outside the loop than it is set only once and never changed any more.
Also you should consider using a ScheduledExecutorService instead of two threads. It is easier to handle and you only need one Thread.
It has nothing to do with where you instantiate it. It's like Date, it holds the static time of the moment you instantiate it.
I'm not sure why you are bothering...
You could, instead just supply a custom DateFormat
public static final DateFormat TIME_FORMAT = new SimpleDateFormat("h:M:s");
private Date currentTime;
Then in your thread...
public void run(){
for(;;){
currentTime = new Date();
try{
Thread.sleep(1000);
}
catch(Exception e){
System.out.println(e);
}
repaint();
}
}
And then in you paint method...
public void paint(Graphics g){
setBackground(randomColor);
g.setFont(font);
g.drawString(TIME_FORMAT.format(currentTime),80,120);
}
But that's just me...
I want to make a Timer that waits 400 MSc and then goes and prints "hi !" (e.g.). I know how to do that via javax.swing.Timer
ActionListener action = new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
System.out.println("hi!");
}
};
plus :
timer = new Timer(0, action);
timer.setRepeats(false);
timer.setInitialDelay(400);
timer.start();
but as I know this definitely is not a good way as this kind of Timer is for Swing works. how to do that in it's correct way? (without using Thread.sleep())
Timer t = new Timer();
t.schedule(new TimerTask() {
#Override
public void run() {
System.out.println("Hi!");
}
}, 400);
You can consider Quartz scheduler, it's a really scalable, easy to learn and to configure solution. You can have a look at the tutorials on the official site.
http://quartz-scheduler.org/documentation/quartz-2.1.x/quick-start
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class currentTime {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
System.out.println( sdf.format(cal.getTime()) );
}
}
TimeUnit.MILLISECONDS.sleep(150L);
is an alternative;
You could also take a look at this Question
Which suggests using a while loop which just waits
or a ScheduledThreadPoolExecutor
I want to have a clock showing current time and refreshes every second. The code I am using is:
int timeDelay = 1000;
ActionListener time;
time = new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
timeLabel.setText(DateTimeUtil.getTime());
/*timeLabel is a JLabel to display time,
getTime() is samll static methos to return formatted String of current time */
}
};
SwingWorker timeWorker = new SwingWorker() {
#Override
protected Object doInBackground() throws Exception {
new Timer(timeDelay, time).start();
return null;
}
};
timeWorker.execute();
What I want to refresh the timeLabel text in another thread other than EDT.
Am I doing it correct? Any other better way?
Also for information, i've added timeLabel to a extendedJPanel which contains few similar kinds of utilities, and is called in another MainJFrame.
You can do this without a SwingWorker, because this is what the Swing Timer is made for.
int timeDelay = 1000;
ActionListener time;
time = new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
timeLabel.setText(DateTimeUtil.getTime());
/* timeLabel is a JLabel to display time,
getTime() is samll static methos to return
formatted String of current time */
}
};
new Timer(timeDelay, time).start();
I used the following program to get the clock.What it turned out to be is a static clock ? Why is it so ?
import java.util.*;
import java.awt.*;
import javax.swing.*;
class tester {
JFrame fr = new JFrame();
JPanel p = new JPanel();
JLabel l = new JLabel();
Date d = new Date();
GregorianCalendar gc = new GregorianCalendar();
tester() {
p.setBackground(Color.red);
l.setVisible(true);
p.add(l);
fr.add(p);
fr.setSize(200,200);
fr.setVisible(true);
startClockThread();
}
public void startClockThread() {
Runnable r = new Runnable() {
#Override
public void run() {
startClock();
}
};
new Thread(r).start();
}
public void startClock() {
l.setVisible(true);
while(true) {
l.setText(gc.get(gc.HOUR) + ":" + gc.get(gc.MINUTE) + ":" + gc.get(gc.SECOND));
System.out.println(gc.get(gc.SECOND));
}
}
public static void main(String args[]) {
new tester();
}
}
GregorianCalendar() Constructs a default GregorianCalendar using the current time in the default time zone with the default locale. Java Doc
You can do this way.
while(true) {
GregorianCalendar gc = new GregorianCalendar();
l.setText(gc.get(gc.HOUR) + ":" + gc.get(gc.MINUTE) + ":" + gc.get(gc.SECOND));
}
Now you should understand why you are getting a static clock !
You only create the GregorianCalendar once, and it never gets updated. So the date is always the same.
there's are big problems apart from the one you have spotted:
dont let threads run wild, they'll freeze the ui eventually
each and every access to a Swing component must happen on the EDT
You can solve both easiest by using a javax.swing.Timer
ActionListener nextSecond = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// get time ...
timeLabel.setText(...);
}
}
new Timer(1000, nextSecond).start();