刷新 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 倒计时不起作用的主要内容,如果未能解决你的问题,请参考以下文章

快速无效计时器不起作用

为啥 MIDI 音符计时不起作用?

表拉刷新不起作用

QWebEngineView:计时器在可见时不起作用

拉动刷新不起作用

快速刷新控制,endRefreshing 不起作用 [重复]