【问题标题】:Can't understand Java Swing Timers. How do I make a 1 time delay?无法理解 Java Swing Timers。如何延迟 1 次?
【发布时间】:2015-01-29 08:09:08
【问题描述】:

我需要在这个程序中暂停一下我正在尝试做的事情。我在Java Swing JFrame 中显示一些文本,重新绘制显示,等待 1.5 秒,然后更改文本。

基本上,我是从这个开始的:

statusLabel.setText(s);    
appFrame.repaint();
Thread.sleep(1500);
statusLabel.setText(y);
appFrame.repaint();

但这不起作用。 Thread.sleep() 将在重绘完成之前调用,这意味着 s 永远不会显示。我读了很多你不应该在swing应用程序中使用Thread.sleep()的地方,因为它会暂停所有线程,甚至是试图重新绘制的线程,并且要暂停由actionPerformed()触发的某些东西,你需要使用Java Swing计时器。

这一切都很好,只是我找不到一个地方可以很好地解释它们的工作原理。因为,据我所知,计时器专门用于在计时器上重复事件。我只想在 2 次重绘之间延迟 1.5 秒。

我试过这样做...

statusLabel.setText(s);    
appFrame.repaint();

Timer timer = new Timer(1500, new ActionListener()
{
    public void actionPerformed(ActionEvent ae)
    {

    }
});

timer.setInitialDelay(1500);
timer.setRepeats(false);
timer.start();

statusLabel.setText(y);
appFrame.repaint();

...在其 actionPerformed 事件中添加一个具有 1.5 秒初始延迟、无重复且无主体的计时器,因此它实际上只等待 1.5 秒,但它没有工作。

【问题讨论】:

标签: java swing timer javax.swing.timer


【解决方案1】:

正如您的示例中的编码,看起来计时器会“工作”,它只是不做任何事情,因为 actionPerformed 方法是空的。您可能会认为timer.start() 会阻塞并等待计时器触发,但实际上它会立即返回。计时器的工作方式是计时器的actionPerformed 方法将在它应该被调用时从UI 线程中调用。将代码放在计时器的actionPerformed 方法中是定期更新 UI 状态的好方法。

【讨论】:

  • 好的,如果它不阻塞并等待定时器触发,那我该怎么做呢?
  • 一般你不应该。您可以使用同步来做到这一点,例如,通过使用从 actionPerformed 方法内部更新的原子变量。但随后它将具有与Thread.sleep 相同的行为。这些都不是好的解决方案。做事的正确方法是重构你的代码以实际使用计时器回调来做任何需要做的事情,而不是等待来自不同线程的超时。
  • 嗯,这……令人困惑。
  • 另外,Thread.sleep 在 Swing 中不会“暂停所有线程”,它可以正常工作。 repaint() 请求重绘,但直到 UI 线程开始安排它,绘画才真正发生,包括实际与计算机的图形系统对话等神奇的东西。如果您在 UI 线程中使用 Thread.sleep 暂停 UI 线程,则绘制将无法正常工作。在 Swing 中实现 UI 的正确方法是永远不要阻塞 UI 线程。如果您出于某种原因需要阻塞或休眠,请为该任务创建一个单独的线程,不要在 UI 线程中进行。
【解决方案2】:

您是否尝试过将statusLabel.setText(y); 放在您的ActionListeneractionPerformed 方法中?

statusLabel.setText(s);    

Timer timer = new Timer(1500, new ActionListener()
{
    public void actionPerformed(ActionEvent ae)
    {
        statusLabel.setText(y);
    }
});

timer.setRepeats(false);
timer.start();

如果这仍然不起作用,请考虑提供 runnable example 来证明您的问题。这将减少混乱并获得更好的响应

更新

您“似乎”想要做的是设置一系列在不同时间触发的事件......而不是使用单独的Timers,您应该使用单个Timer,例如循环,每次它滴答作响时,你检查它的状态并做出一些关于应该做什么的决定,例如......

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Flashy {

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

    public Flashy() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public static class TestPane extends JPanel {

        private JLabel flash;
        private JButton makeFlash;

        protected static final Color[] FLASH_COLORS = new Color[]{Color.BLUE, Color.RED, Color.GREEN, Color.YELLOW};
        protected static final int[] FLASH_DELAY = new int[]{1000, 2000, 3000, 4000};
        private int flashPoint;

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

            flash = new JLabel("Flash");
            flash.setOpaque(true);
            makeFlash = new JButton("Make Flash");

            add(flash, gbc);
            add(makeFlash, gbc);

            makeFlash.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    flashPoint = -1;
                    Timer timer = new Timer(0, new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            Timer timer = ((Timer)e.getSource());
                            flashPoint++;
                            if (flashPoint < FLASH_COLORS.length) {
                                flash.setBackground(FLASH_COLORS[flashPoint]);
                                System.out.println(FLASH_DELAY[flashPoint]);
                                timer.setDelay(FLASH_DELAY[flashPoint]);
                            } else {
                                flash.setBackground(null);
                                timer.stop();
                                makeFlash.setEnabled(true);
                            }
                        }
                    });
                    timer.setInitialDelay(0);
                    timer.start();                  
                    makeFlash.setEnabled(false);
                }
            });

        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

    }

}

现在,如果您想做一些非常奇特的事情,您可以在给定的时间段内设计一系列关键帧。

这意味着您可以更改动画的持续时间,而无需更改任何其他代码,例如...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Flashy {

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

    public Flashy() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public static class TestPane extends JPanel {

        private JLabel flash;
        private JButton makeFlash;

        protected static final Color[] FLASH_COLORS = new Color[]{Color.BLUE, Color.RED, Color.GREEN, Color.YELLOW};
        protected static final double[] FLASH_DELAY = new double[]{0, 0.2, 0.4, 0.6};

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

            flash = new JLabel("Flash");
            flash.setOpaque(true);
            makeFlash = new JButton("Make Flash");

            add(flash, gbc);
            add(makeFlash, gbc);

            makeFlash.addActionListener(new ActionListener() {
                private int playTime = 10000;
                private long startTime;
                private int currentFrame = -1;

                @Override
                public void actionPerformed(ActionEvent e) {
                    startTime = System.currentTimeMillis();

                    Timer timer = new Timer(50, new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            Timer timer = ((Timer) e.getSource());
                            long now = System.currentTimeMillis();
                            long duration = now - startTime;

                            double progress = (double) duration / (double) playTime;
                            int keyFrame = 0;
                            for (keyFrame = 0; keyFrame < FLASH_DELAY.length; keyFrame++) {

                                double current = FLASH_DELAY[keyFrame];
                                double next = 1d;
                                if (keyFrame + 1 < FLASH_DELAY.length) {
                                    next = FLASH_DELAY[keyFrame + 1];
                                }

                                if (progress >= current && progress < next) {
                                    break;
                                }

                            }

                            if (keyFrame < FLASH_COLORS.length) {

                                flash.setBackground(FLASH_COLORS[keyFrame]);

                            }

                            if (duration >= playTime) {
                                timer.stop();
                                makeFlash.setEnabled(true);
                                flash.setBackground(null);
                            }

                        }
                    });
                    timer.setInitialDelay(0);
                    timer.start();
                    makeFlash.setEnabled(false);
                }
            });

        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

    }

}

一个更高级的概念,在 this answer 中进行了演示

【讨论】:

  • 虽然这适用于这个特定的例子,但它不适用于我正在尝试做的其他人。例如,另一个设置文本 3 次,第 1 次和第 2 次之间有 1 秒的延迟,第 2 次和第 3 次之间有 0.5 秒的延迟。从我的实验和 Atsby 所说的来看,它几乎会同时将计时器排队,0.5 秒将首先出现,1 秒延迟将在第 2 次出现。虽然我可以在第二个上放 1.5 秒以便它按时启动,但我并不是在尝试学习如何硬编码,而是在尝试学习如何让线程在摇摆中正确休眠。
  • “例如,另一个设置文本 3 次,第 1 次和第 2 次之间有 1 秒的延迟” - 使用 Timer 中的计数器来跟踪位置你可以。 “根据我的实验,根据 Atsby 的说法,它几乎会同时将计时器排队” - 然后使用单个 Timer 并在其中编写逻辑代码以提供您需要的更改Timer 被触发的次数
  • “我正在尝试学习如何让线程在摇摆中正常睡眠” - 简单的答案是,不要
猜你喜欢
  • 1970-01-01
  • 2019-11-27
  • 2017-04-08
  • 2014-07-12
  • 2021-04-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多