I'm trying to get a JLabel to display the date and update every second. To do this I'm using Swing's Timer class and implementing my own class called DateTimer. DateTimer is also an ActionListener.
This is DateTimer:
public class DateTimer implements ActionListener {
private int delay;
private JLabel label;
private Calendar cal;
public DateTimer(int delay, JLabel label) {
this.delay = delay;
this.label = label;
cal = Calendar.getInstance();
new Timer(this.delay, this).start();
}
public void actionPerformed(ActionEvent e) {
this.label.setText(this.cal.getTime().toString());
}
}
I call this from somewhere else in my code like this:
new DateTimer(1000, this.label);
I get the date to display once, then it doesn't ever update.
I'm new to Java GUIs and handling actions so please excuse my ignorance.
java.util.Calendar.getInstance() returns an object representing the current date and time at the time it is created. It doesn't update automatically as you are assuming.
Related
I don't know how to put this code insid emy programs . Likee..if I click on the button or by mouse in JPanel (that's inside the original panel), the program will run
static class Action implements ActionListener(){
public void actionPerformed (ActionEvent e){
long serialVersionUID = 1L;
Timer timer;
long startTime = -1;
long duration = 1200000 ;
JLabel label = new JLabel;
timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (startTime < 0) {
startTime = System.currentTimeMillis();
}
long now = System.currentTimeMillis();
long clockTime = now - startTime;
if (clockTime >= duration) {
clockTime = duration;
timer.stop();
}
SimpleDateFormat df = new SimpleDateFormat("mm:ss:SSS");
label.setText(df.format(duration - clockTime));
}
});
This is a typical problem of new programmers finding code then copying it and pasting it wherever they think is might work. As you've already realized....it's not that simple otherwise you obviously wouldn't be here seeking assistance.
There are a number of things going wrong with your provided code and this is exactly why I'll provide you with code to show you how this can work using a Local Timer within a JButton Action Event and point out some of the specific problems so that you can realize them for later on should you encounter this sort of thing again.
You don't need to declare your timer variable and then initialize it later on down the road. You can declare and initialize the timer variable right on the same line, for example:
final Timer timer = new Timer(10, new ActionListener() {
..................
..................
..................
});
Your Long data type startTime variable will never work within a Local Timer or any Inner (local) Class for that matter where value changes will need to be made. For your startTime variable to be used within the Local Timer Class it needs to carry full scope there and to do that the variable needs to be declared as final. Uhhh, there is a problem with this though, you can not change the value within a final variable which is a requirement for this countdown timer to function properly. If we don't declare the startTime variable as final then we'll just end up with a "local variables referenced from an inner class must be final or effectively final" compile error. The solution to this then is to declare and initialize the startTime variable within the Local Timer class itself like this:
final Timer timer = new Timer(10, new ActionListener() {
long startTime = -1;
#Override
public void actionPerformed(ActionEvent event) {
..................
..................
}
});
That solves that little problem. Now you have yet another problem, you can't just up and stop your Timer from within itself using the Timer.stop() method as you've done (ie: timer.stop(); ) within the Timer's actionPerformed event for the simple reason the variable timer has no proper scope there and can't see that it's been initialized. Yes, it'll probably compile and it'll most likely run right up until the timer.stop() method is called and then you'll end up getting a Runtime Exception ("variable timer might not have been initialized"). Not good, and don't think about catching the exception and letting bygones be bygones because your ultimately not stopping the Timer (crappy practice). You just need to get a little fancier with your stop and this will do the trick:
((Timer)event.getSource()).stop();
//event is the ActionEvent variable (usually evt in some IDE's).
Now, your duration variable, there is nothing wrong with this if you like to supply long number values for even 1 minute of timing. Works great if that's your thing but for me personally, I like simply entering 60 instead of 60000. It really doesn't matter if the value will be hard coded into place but if it will actually be a supplied value (regardless from where) then it's just better to supply seconds (IMHO) which would of course entail the use of an additional variable, for example:
int seconds = 10;
final long duration = seconds * 1000;
Now you only need to supply the number of seconds you want your Countdown Timer to be set at.
Another problem you have is that you've declared a variable named label to represent a JLabel but nowhere do you either reference that variable to an actual JLabel somewhere or add it to a frame or panel component. I thinking that you've already installed a JLabel within your GUI (if you have a GUI) in which case you will need to supply that JLabel variable name when you declare the label variable, like this:
JLabel label = (JLabel)jLabel1;
This way the label variable represents the JLabel named jLabel1 within your GUI.
And finally (and this is a biggy) your Timer simply wont run if you don't tell it to do so. Where do you do this, well, right under your Local Timer Class of course :) like this:
final Timer timer = new Timer(10, new ActionListener() {
long startTime = -1;
#Override
public void actionPerformed(ActionEvent event) {
..................
..................
}
});
timer.start();
Anyways, here's is the full code as promised:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
int seconds = 10; // supply timer duration in seconds
final long duration = seconds * 1000; // calculate to milliseconds
JLabel label = (JLabel)jLabel1; //whichever JLabel is in GUI
final Timer timer = new Timer(10, new ActionListener() {
long startTime = -1;
#Override
public void actionPerformed(ActionEvent event) {
if (startTime < 0) {
startTime = System.currentTimeMillis();
}
long now = System.currentTimeMillis();
long clockTime = now - startTime;
if (clockTime >= duration) {
clockTime = duration;
((Timer)event.getSource()).stop(); // stop the Timer
label.setText("Time Is UP"); // remove if you want or maybe just ""
return;
}
SimpleDateFormat df = new SimpleDateFormat("mm:ss:SSS");
label.setText(df.format(duration - clockTime));
}
});
timer.start();
}
I'm new into programming and I'm having some trouble with this.
The problem is, I'm using the Swing palette to create an assignment where I'm using a JDialog to display a timer at the same time of another frame, when I dispose this frame to change to another and return to the previous one the timer in the JDialog overlaps the first one that was running, and I couldn't managed to fix it.
Here's the code.
MAIN
public static void main(String[] args) {
Panel0 screen=new Panel0();
screen.setTitle("");
screen.setLocationRelativeTo(screen);
screen.setVisible(true);
}
1st FRAME
public class Panel0 extends javax.swing.JFrame {
Panel s=new Panel();
private void fisica1ActionPerformed(java.awt.event.ActionEvent evt) {
s.time();
s.setTitle("FISIC I");
s.setLocationRelativeTo(s);
s.setVisible(rootPaneCheckingEnabled);
s.dialog.setVisible(rootPaneCheckingEnabled);
dispose();
}
2nd FRAME
public class Panel extends javax.swing.JFrame {
private void EndActionPerformed(java.awt.event.ActionEvent evt) {
dialog.dispose();
dialog.setDefaultCloseOperation(0);
Panel0 pan=new Panel0();
pan.setLocationRelativeTo(p1);
pan.setVisible(rootPaneCheckingEnabled);
dispose();
}
void time(){
t=new Timer(1,new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (startTime<0) {
startTime=System.currentTimeMillis();
}
long now = System.currentTimeMillis();
long clockTime = now - startTime;
if (clockTime >= duration) {
clockTime = duration;
t.stop();
}
SimpleDateFormat sdf=new SimpleDateFormat("mm:ss:SS");
clock.setText(sdf.format(duration-clockTime));
}
});
t.setInitialDelay(0);
if (!t.isRunning()) {
startTime = -1;
t.start();
}
}
I omitted the inizialization of the Timer and such, because I don't think that's the problem.
To clarify something: Once I close the 2nd frame the 1st opens and gives me options to repeat this process over and over, and everytime the JDialog named "dialog" overlaps with its data (you can see the numbers of the clock overlaping).
dispose() does not means that you will "destroy" the object or clear its state. It means that you will release graphics resources attached to that frame (low level window handle and stuff). It still can be reused with setVisible(true)
I assume that you want to reuse our popup - this is just fine, but I think that you are forgetting to stop the "disposed" timer thus every new timer you create on action will be exposed to so called "racing conditions".
Timers are simple background task and they must be stopped explicitly - it will not be done by itself.
Every call to s.time(); starts new timer without stopping previous one.
Simply speaking: you have multiple timers updating the same text field.
Solution: Stop previous timer before running new OR restart previous timer.
I'm trying to make a clock using as few resources as possible and just relying on my (limited) knowledge of Java. I've come to a road block however. The clock I wrote works, except rather than the text in the jlabel being replaced, it overlaps itself. I've tried fixing this by clearing the value of timeStamp, but it doesn't seem to be working.
public class Clock extends JFrame{
public static void main (String args[]) {
Clock gui = new Clock();
gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gui.setSize(50,50);
gui.setVisible(true);
gui.setTitle("Clock");
int a = 1;
while (a == 1){
String timeStamp = new SimpleDateFormat("hh:mm:ss a").format(Calendar.getInstance().getTime());
JLabel label;
label = new JLabel();
label.setText(String.valueOf(timeStamp));
timeStamp = "";
gui.add(label);
label.revalidate();
}
}
}
You should not be creating a new JLabel every iteration.
JLabel label = new JLabel();
gui.add(label);
while (a == 1){
String timeStamp = new SimpleDateFormat("hh:mm:ss a").format(Calendar.getInstance().getTime());
label.setText(String.valueOf(timeStamp));
timeStamp = "";
label.revalidate();
}
You should use a SwingWorker to update the clock. Currently you're doing it on the event dispatch thread and thus interfere with the UI rendering.
Besides that you should reuse the label instead of creating a new one for each timestamp. Currently you're stacking labels on top of each other since gui.add() will just add the new label and won't remove the old ones.
Why are you creating a new JLabel in every iteration of the loop?
Don't do that.
Just create a single label in Clock's constructor.
Also, changing the label's text should be done on the event thread, not the main thread.
While the code you wrote "works", you're missing some things to make a stable Swing GUI.
You must always start a Swing application using the SwingUtilities invokelater method. This puts the creation and the execution of the Swing components on the Event Dispatch thread (EDT).
I separated the creation of the GUI from the execution of the GUI. Separation of concerns makes coding each part easier.
In the Timer Runnable, I again use the SwingUtilities invokeLater method to make sure that the updating of the JTextField with the time happens on the EDT.
I stop the Thread before I exit. Generally, you should stop any threads you start, and not rely on the JVM to clean up for you.
Here's the clock.
And here's the code.
package com.ggl.testing;
import java.awt.Color;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class SimpleClock implements Runnable {
private JFrame frame;
private JPanel panel;
private JTextField clockDisplay;
private Timer timer;
#Override
public void run() {
frame = new JFrame("Clock");
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent event) {
exitProcedure();
}
});
panel = new JPanel();
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 6));
clockDisplay = new JTextField(12);
clockDisplay.setEditable(false);
clockDisplay.setHorizontalAlignment(JTextField.CENTER);
panel.add(clockDisplay);
frame.add(panel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
timer = new Timer(this);
new Thread(timer).start();
}
public void exitProcedure() {
timer.setRunning(false);
frame.dispose();
System.exit(0);
}
public void setText(String text) {
clockDisplay.setText(text);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Clock());
}
public class Timer implements Runnable {
private volatile boolean running;
private SimpleClock clock;
private SimpleDateFormat timeFormat;
public Timer(SimpleClock clock) {
this.clock = clock;
this.running = true;
this.timeFormat = new SimpleDateFormat("h:mm:ss a");
}
#Override
public void run() {
while (running) {
displayTime();
sleep();
}
}
public void displayTime() {
final Calendar calendar = Calendar.getInstance();
Date date = calendar.getTime();
final String s = timeFormat.format(date);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
clock.setText(s);
}
});
}
public void sleep() {
try {
Thread.sleep(200L);
} catch (InterruptedException e) {
}
}
public synchronized void setRunning(boolean running) {
this.running = running;
}
}
}
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 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.