为什么我的循环GUI计时器没有出现?

时间:2017-05-22 02:49:18

标签: java swing thread-sleep

我试图制作一个GUI计时器而不使用 javax.swing.Timer(一种奇怪的任务),但我无法使其正常工作。它应该让线程休眠1秒钟,将{1}加1,然后重复(无限)。当我运行程序时,图标会显示,但窗口不会出现。我猜测我的错误是在seconds行或该区域,但我不确定为什么它不起作用。 Thread.sleep(1000);与swing应用程序不兼容吗?我需要多线程吗?这是我的计划:

Thread.sleep(millis)

修改
我注意到当我在方法import java.awt.*; import javax.swing.*; public class GUITimer extends JFrame { private static final long serialVersionUID = 1L; private int seconds = 0; public GUITimer() { initGUI(); pack(); setVisible(true); setResizable(false); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } private void initGUI(){ JLabel title = new JLabel("Timer"); Font titleFont = new Font(Font.SERIF, Font.BOLD, 32); title.setFont(titleFont); title.setHorizontalAlignment(JLabel.CENTER); title.setBackground(Color.BLACK); title.setForeground(Color.WHITE); title.setOpaque(true); add(title, BorderLayout.NORTH); JLabel timeDisplay = new JLabel(Integer.toString(seconds));//this label shows seconds add(timeDisplay, BorderLayout.CENTER); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } seconds++; initGUI(); } public static void main(String[] args) { try { String className = UIManager.getCrossPlatformLookAndFeelClassName(); UIManager.setLookAndFeel(className); } catch (Exception e) {} EventQueue.invokeLater(new Runnable() { public void run() { new GUITimer(); } }); } } 中打印seconds到控制台时,它会正确地逐步打印它们一秒钟。所以看起来像:

initGUI()

每秒后打印private void initGUI() { System.out.println(seconds); //... 的值(seconds应如何)。这表明我的循环工作正常,我的JLabel也可以。我现在唯一的问题是框架没有出现。

2 个答案:

答案 0 :(得分:5)

您的主窗口没有出现,因为您在构造函数中调用了无限递归。将不会创建GUITimer并锁定主线程。

您需要使用多线程来实现此目标。绘制时间的主线程,第二个线程增量和将值放到标签

例如:

import javax.swing.*;
import java.awt.*;

public class GUITimer extends JFrame
{
    private static final long serialVersionUID = 1L;
    private int seconds = 0;
    private Thread timerThread;
    private JLabel timeDisplay;

    public GUITimer()
    {
        initGUI();
        pack();
        setVisible(true);
        setResizable(false);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    private void initGUI()
    {
        JLabel title = new JLabel("Timer");
        Font titleFont = new Font(Font.SERIF, Font.BOLD, 32);
        title.setFont(titleFont);
        title.setHorizontalAlignment(JLabel.CENTER);
        title.setBackground(Color.BLACK);
        title.setForeground(Color.WHITE);
        title.setOpaque(true);
        add(title, BorderLayout.NORTH);
        timeDisplay = new JLabel(Integer.toString(seconds));//this label shows seconds
        add(timeDisplay, BorderLayout.CENTER);
    }

    public void start()
    {
        seconds = 0;
        timerThread = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                while(true)
                {
                    timeDisplay.setText(Integer.toString(seconds++));
                    try
                    {
                        Thread.sleep(1000L);
                    }
                    catch(InterruptedException e) {}
                }
            }
        });
        timerThread.start();
    }

    public void stop()
    {
        timerThread.interrupt();
    }

    public static void main(String[] args)
    {
        try
        {
            GUITimer timer = new GUITimer();
            timer.start();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
}

答案 1 :(得分:4)

核心问题是,您通过不断调用initGUI来阻止用户界面,最终将失败并显示StackOverFlowException,因为方法调用永远不会结束

首选是使用Swing Timer,但由于您已经声明不想这样做,更好的解决方案是使用SwingWorker,原因是 - Swing不是线程安全的,SwingWorker提供了一种方便的机制,允许我们安全地更新UI。

因为Swing TimerThead.sleep只保证最小延迟,它们不是衡量时间流逝的可靠方法,所以最好使用Java 8的Date / Time API代替

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JLabel label = new JLabel("00:00:00");
        private TimeWorker timeWorker;

        public TestPane() {
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            add(label, gbc);

            JButton button = new JButton("Start");
            add(button, gbc);

            button.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (timeWorker == null) {
                        timeWorker = new TimeWorker(label);
                        timeWorker.execute();
                        button.setText("Stop");
                    } else {
                        timeWorker.cancel(true);
                        timeWorker = null;
                        button.setText("Start");
                    }
                }
            });
        }
    }

    public class TimeWorker extends SwingWorker<Void, Duration> {

        private JLabel label;

        public TimeWorker(JLabel label) {
            this.label = label;
        }

        @Override
        protected Void doInBackground() throws Exception {
            LocalDateTime startTime = LocalDateTime.now();
            Duration totalDuration = Duration.ZERO;
            while (!isCancelled()) {
                LocalDateTime now = LocalDateTime.now();
                Duration tickDuration = Duration.between(startTime, now);
                publish(tickDuration);
                Thread.sleep(500);
            }

            return null;
        }

        @Override
        protected void process(List<Duration> chunks) {
            Duration duration = chunks.get(chunks.size() - 1);
            String text = format(duration);
            label.setText(text);
        }

        public String format(Duration duration) {
            long hours = duration.toHours();
            duration = duration.minusHours(hours);
            long minutes = duration.toMinutes();
            duration = duration.minusMinutes(minutes);
            long millis = duration.toMillis();
            long seconds = (long)(millis / 1000.0);

            return String.format("%02d:%02d:%02d", hours, minutes, seconds);
        }
    }
}