刷新 JComponent 倒计时不起作用
Posted
技术标签:
【中文标题】刷新 JComponent 倒计时不起作用【英文标题】:Refreshing JComponent to make a count down isn't working 【发布时间】:2013-12-08 10:09:15 【问题描述】:我制作了这个示例来对 java 对话消息倒计时 30 秒。不知何故,消息组件没有刷新,直到现在我才明白为什么。如果时间正在更新并且组件重新绘制它应该可以工作。我尝试重新绘制 message.repaint()
和 messageFrame.repaint()
两个组件,但都没有工作。
下面的代码可以运行了。
import java.awt.Component;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Chronometer
private static final Insets INSETS = new Insets(1, 1, 1, 1);
public Chronometer()
double timeToNext = System.currentTimeMillis() + 30000;
double timeDifference = 30000;
JFrame messageFrame = new JFrame();
messageFrame.setLayout(new GridBagLayout());
messageFrame.setTitle("Warning");
JLabel message = new JLabel("Preventing connection block. Next query in " + timeDifference/1000 + " seconds.");
messageFrame.setSize(1000,1000);
messageFrame.setLocationRelativeTo(null);
addComponent(messageFrame, message, 0, 0, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.CENTER);
messageFrame.setVisible(true);
while(timeDifference > 0)
timeDifference = timeToNext - System.currentTimeMillis();
messageFrame.repaint();
//message.repaint();
private static void addComponent(Container container, Component component, int gridx, int gridy,
int gridwidth, int gridheight, int anchor, int fill)
GridBagConstraints gbc = new GridBagConstraints(gridx, gridy, gridwidth, gridheight, 1.0, 1.0,
anchor, fill, INSETS, 0, 0);
container.add(component, gbc);
public static void main(String[] args)
Chronometer c = new Chronometer();
【问题讨论】:
【参考方案1】:你走错路了。在构造函数中使用 while 循环是个坏主意。 您应该改用计时器,因为这正是 Chronometer 的含义。我拿了你的代码并用计时器快速重写了它,并利用观察者模式来更新标签。
使用实现 Observer 接口的控制器。
import java.awt.Component;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Controller implements Observer
private static final Insets INSETS = new Insets(1, 1, 1, 1);
private Chronometer chrono = new Chronometer();
private final JLabel message = new JLabel();
public Controller()
final JFrame messageFrame = new JFrame("Warning");
messageFrame.setLayout(new GridBagLayout());
messageFrame.setSize(1000,1000);
messageFrame.setLocationRelativeTo(null);
addComponent(messageFrame, message, 0, 0, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.CENTER);
messageFrame.setVisible(true);
chrono.addObserver(this);
public void startChrono(int delay)
chrono.startCountDown(delay);
public void refreshLabel()
SwingUtilities.invokeLater(new Runnable()
@Override
public void run()
message.setText("Preventing connection block. Next query in " + chrono.getTime()/1000 + " seconds.");
);
@Override
public void update(Observable o, Object arg)
refreshLabel();
private static void addComponent(Container container, Component component, int gridx, int gridy, int gridwidth, int gridheight, int anchor, int fill)
GridBagConstraints gbc = new GridBagConstraints(gridx, gridy, gridwidth, gridheight, 1.0, 1.0,anchor, fill, INSETS, 0, 0);
container.add(component, gbc);
创建一个带有计时器的计时器类:
import java.util.Observable;
import java.util.Timer;
import java.util.TimerTask;
public class Chronometer extends Observable
private Timer timer = new Timer();
private final int startTime = 30000;
private int time = 30000;
public void decreaseTime()
if (time > 0) time--;
public int getTime()
return time;
public void reset()
time = startTime;
public void startCountDown(int delay)
timer.schedule(new TimerTask()
@Override
public void run()
decreaseTime();
setChanged();
notifyObservers();
, delay,1);
最后启动你的控制器
public class Main
public static void main(String[] args)
SwingUtilities.invokeLater(new Runnable()
@Override
public void run()
new Controller().startChrono(1);
);
【讨论】:
谢谢,我会分析这段代码,当我尝试在我的主程序上执行此操作时,我只是实现了一个可运行的 cos,组件的更新变为空白,呵呵,你刚刚想出了另一部分我正在构建的解决方案哈哈哈 ty 现在,这几乎是正确的。只需要修改以在事件调度线程中创建和访问 swing 组件 - 最好使用 swing Timer 而不是通用计时器,以便在 EDT 中进行标签更新。 @kiheru 我只是想指出在构造函数中使用 while 循环并在他的代码中添加一个计时器来演示是一个坏主意。如果您使用摆动计时器或其他计时器,则无关紧要,因为这是一个实现细节,在这种情况下,重要的是原理。 EDT == event dispatch thread。计时器的类型很重要,因为更改标签必须在 EDT 中完成。摆动计时器会自动执行此操作,对于java.util.Timer
,必须使用 SwingUtilities.invokeLater()。
这不是唯一的更新。问题是在 EDT 之外调用 message.setText()
(现在直接通过观察者。如果您不确定,请与 SwingUtilities.isEventDispatchThread()
联系)。那不是线程安全的。它“通常”似乎可以工作,但是 gui 程序越复杂,它就越有可能以看似随机的方式中断,除非它遵守线程规则。用invokeLater()
包裹那个电话就可以了。同样,GUI 的创建应该在 EDT 中完成。【参考方案2】:
我认为您需要手动更新 JLabel 文本。尝试像这样更改您的 while 循环:
while(timeDifference > 0)
timeDifference = timeToNext - System.currentTimeMillis();
message.setText("Preventing connection block. Next query in " + timeDifference/1000 + " seconds.");
【讨论】:
我不认为while循环是解决这个问题的方法,检查我的答案。以上是关于刷新 JComponent 倒计时不起作用的主要内容,如果未能解决你的问题,请参考以下文章