Reputation: 3
I was trying to set up a simple time ticker on a JFrame. I am able to display the initial value of counter, but any subsequent change doesn't update on the JFrame. Any nudge towards an answer to what I am doing wrong would be appreciated. I think it is a problem of repaint() not getting called, but I either end up with errors, or nothing if I try putting it in.
package com.game.ryan;
import java.awt.Dimension;
import javax.swing.JFrame;
class Screen extends JFrame{
private Dimension d = new Dimension(800,600);
private JFrame f;
public Screen(){
f = new JFrame();
f.setIgnoreRepaint(false);
f.setResizable(false);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setMinimumSize(d);
f.setLocationRelativeTo(null);
f.add(new MyPanel());
f.pack();
f.setVisible(true);
}
public static void main(String[] args){
Screen s = new Screen();
}
}
I also have:
package com.game.ryan;
import java.awt.Graphics;
import javax.swing.JPanel;
public class MyPanel extends JPanel{
private OneThread ot = new OneThread();
private int counter = ot.getThreadCounter();
public MyPanel(){
Thread t1 = new Thread(new OneThread());
t1.start();
}
public void paintComponent(Graphics g){
g.drawString("TIME: ", 10, 20);
g.drawString(Integer.toString(counter), 50, 20);
}
}
and finally
package com.game.ryan;
public class OneThread implements Runnable{
private int counter = 45;
public OneThread(){
}
@Override
public void run() {
for(int x = 0; x >= 0; x++){
try{
Thread.sleep(1000);
counter++;
x++;
System.out.println(counter);
}catch(Exception e){
e.printStackTrace();
}
}
}
public int getThreadCounter(){
return counter;
}
}
I am getting an increasing counter on the console so I guess that part is working correctly.
Expected result was for the counter to display correctly in the JFrame (updating every 1000ms).
Upvotes: 0
Views: 1424
Reputation: 347314
I don't see anywhere you tell the UI it should update itself.
You're also going a long way out of your way to replicate what is already available within the APIs.
Swing and threads need careful consideration. Swing uses a single threaded model for managing all the updates to the UI. It is expected that all iterations with the UI will be done within the context of the thread (AKA The Event Dispatching Thread).
This means any time you want to create or update the UI from any other thread, you need to synchronize the calls back to the EDT.
While there are a number of ways to achieve this, the simplest in your case in the use of javax.swing.Timer
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class LabelClock {
public static void main(String[] args) {
new LabelClock();
}
protected static final DateFormat DATE_FORMAT = new SimpleDateFormat("HH:mm.ss");
private JLabel clock;
public LabelClock() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
clock = new JLabel();
tick();
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
frame.add(clock);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Timer timer = new Timer(500, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
tick();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
});
}
protected void tick() {
clock.setText(DATE_FORMAT.format(new Date()));
}
}
Upvotes: 2
Reputation: 285405
You may wish to re-think your design:
Upvotes: 2