【问题标题】:How to create a delay in Swing如何在 Swing 中创建延迟
【发布时间】:2021-04-02 15:44:57
【问题描述】:

我做了一个二十一点游戏,我希望 AI 玩家在拿牌之间暂停一下。我尝试简单地使用 Thread.sleep(x),但这会使其冻结,直到 AI 玩家拿完所有的牌。我知道 Swing 不是线程安全的,所以我查看了 Timers,但我不明白如何使用它。这是我当前的代码:

while (JB.total < 21) {

          try {
            Thread.sleep(1000);
          } catch (InterruptedException ex) {
            System.out.println("Oh noes!");
          }

          switch (getJBTable(JB.total, JB.aces > 0)) {
            case 0:
              JB.hit();
              break;
            case 1:
              break done;
            case 2:
              JB.hit();
              JB.bet *= 2;
              break done;
          }
        }

顺便说一句,hit();方法更新 GUI。

【问题讨论】:

    标签: java swing sleep delay thread-sleep


    【解决方案1】:

    所以我查看了 Timers,但我不明白我怎么能用它来做这个

    计时器是解决方案,因为正如您所说,您正在更新应该在 EDT 上完成的 GUI。

    我不确定您的担忧是什么。你发一张牌并启动计时器。当计时器触发时,您决定拿另一张牌或持有。当您按住停止计时器时。

    【讨论】:

    • 谢谢,但我能否给我一些示例代码,说明如何为此使用计时器?我之前试过,它抛出了一个错误,我忘记了到底是什么。
    • @user920769 再次查看(并仔细阅读;)错误,再试一次
    【解决方案2】:

    下面的代码显示了一个带有 JTextArea 和一个 JButton 的 JFrame。当按钮被点击时,定时器会重复发送事件(它们之间有第二个延迟)到与按钮相关的 actionListener,它会在当前时间附加一行。

    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.util.Calendar;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JTextArea;
    import javax.swing.Timer;
    
    
    public class TimerTest extends JFrame implements ActionListener{
    
        private static final long serialVersionUID = 7416567620110237028L;
        JTextArea area;
        Timer timer;
        int count; // Counts the number of sendings done by the timer
        boolean running; // Indicates if the timer is started (true) or stopped (false)
    
        public TimerTest() {
            super("Test");
            setBounds(30,30,500,500);
            setDefaultCloseOperation(EXIT_ON_CLOSE);
            setLayout(null);
    
            area = new JTextArea();
            area.setBounds(0, 0, 500, 400);
            add(area);
    
            JButton button = new JButton("Click Me!");
            button.addActionListener(this);
            button.setBounds(200, 400, 100, 40);
            add(button);
    
            // Initialization of the timer. 1 second delay and this class as ActionListener
            timer = new Timer(1000, this);
            timer.setRepeats(true); // Send events until someone stops it
            count = 0; // in the beginning, 0 events sended by timer
            running = false;
            System.out.println(timer.isRepeats());
            setVisible(true); // Shows the frame
        }
    
        public void actionPerformed(ActionEvent e) {
            if (! running) {
                timer.start();
                running = true;
            }
            // Writing the current time and increasing the cont times
            area.append(Calendar.getInstance().getTime().toString()+"\n");
            count++;
            if (count == 10) {
                timer.stop();
                count = 0;
                running = false;
            }
        }
    
        public static void main(String[] args) {
            // Executing the frame with its Timer
            new TimerTest();
        }
    }
    

    好吧,这段代码是如何使用 javax.swig.Timer 对象的示例。与问题的具体情况有关。停止计时器的 if 语句必须改变,而且很明显,actionPerformed 的动作。以下片段是解决方案 actionPerformed 的骨架:

    public void actionPerformed(ActionEvent e) {
        if (e.getComponent() == myDealerComponent()) {
        // I do this if statement because the actionPerformed can treat more components
            if (! running) {
                timer.start();
                runnig = true;
            }
            // Hit a card if it must be hitted
            switch (getJBTable(JB.total, JB.aces > 0)) {
              case 0:
                  JB.hit();
                  break;
              case 1:
                  break done;
              case 2:
                  JB.hit();
                  JB.bet *= 2;
                  break done;
            }
            if (JB.total >= 21) { // In this case we don't need count the number of times, only check the JB.total 21 reached
                timer.stop()
                running = false;
            }
    
        }
    }
    

    恕我直言,这解决了问题,现在@user920769 必须考虑将 actionListener 和启动/停止条件放在哪里...

    @kleopatra:感谢您向我展示了这个计时器类的存在,我对它一无所知,这太棒了,可以将很多有任务的事情变成一个摇摆应用程序:)

    【讨论】:

    • 非常感谢您提供的示例,但我在以下几行中遇到错误: timer = new Timer(1000, this); timer.setRepeats(true);分别说它找不到合适的构造函数或方法。它们被弃用了吗?
    • 你是否导入了 Timer 类?即使在最后一个版本中,这些方法也没有被弃用,因此这似乎是你的错误。 Here the Java7 ApiDoc
    • 你的代码中有很多东西需要清理:1) 不要implements ActionListener!将 Lambda 用于button.addActionListener( e -&gt; xyButtonClicked()); 这样,您可以轻松分离关注点,而无需检查 Action 的来源。特别是在您想手动调用它们的情况下。 2)在您的成员变量前面加上可见性(私有),最后在任何地方都可以!如果可能,在变量声明时初始化它们。 3) myDealerComponent() 是你能给函数起的最糟糕的名字,因为它缺少 get 并且可能有一些不起眼的魔法 invo
    【解决方案3】:

    好吧,关于计时器的简要说明。

    首先,您的类中需要一个 java.util.Timer 变量,而您的项目中需要另一个从 java.util.TimerTask 扩展的类(我们称之为 Tasker)。

    Timer变量的初始化就是这么简单:

    Timer timer = new Timer();
    

    现在是 Tasker 类:

    public class Tasker extends TimerTask {
        @Override
        public void run() {
            actionToDo(); // For example take cards 
        }
    
        // More functions if they are needed
    }
    

    最后,安装定时器及其相关的Tasker:

    long delay = 0L;
    long period = pauseTime;
    timer.schedule(new Tasker(),delay,period);
    

    调度功能指示如下: 第一个参数:每个周期毫秒执行的操作(执行 TimerTask 类或其扩展的运行函数) 第二个参数:计时器必须启动的时间。在这种情况下,它在调用 schedule 函数时启动。以下示例表示调用 schedule 函数后 1 秒开始:timer.schedule(new Tasker(),1000,period); 第三个参数:Tasker.run() 函数的一次调用与下一次调用之间的毫秒数。

    我希望你理解这个微教程:)。如果您有任何问题,请询问更详细的信息!

    亲切的问候!

    【讨论】:

    • (已编辑以消除绝对主义 :-) 实际上 - 您很少在 Swing 中使用 util.Timer,而是使用 swingx.Timer 或(对于更复杂的后台任务)SwingWorker
    • @kleopatra swingx.Timer(挠头)DYM a javax.swing.Timer?不能说我遇到过另一个。
    • 好吧,我把代码和 util.Timer 放在一起,因为这是我一年前在一个项目中使用的。我的项目是一场排球比赛,我们使用上述结构重新计算信息并每 0.04 秒刷新一次窗口。我不知道如何使用 swingx.Timer,但这段代码在图形应用程序中可以正常工作。它不会冻结窗口,让用户毫无问题地做事。 =)
    • 对不起,我的错,我的错(潜意识狭隘;-) - @Andrew 猜对了,我的意思是 javax.swing.Timer 和 java.util.Timer
    • 好吧,我一直在阅读 swing.Timer API,并且我已经使用 Timer 编写了一个代码,可以帮助@user920769。由于答案的扩展,我把它放在一个新的答案中。我不编辑这个,因为虽然它不是最好的方法,但 util.Timer 很容易做到这一点。
    【解决方案4】:

    我认为this tutorial 中明确了如何使用计时器来实现您想要的,而无需处理线程。

    【讨论】:

    • esta respuesta es de la buena
    猜你喜欢
    • 2015-02-15
    • 1970-01-01
    • 2011-03-04
    • 2014-02-28
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 2021-09-07
    相关资源
    最近更新 更多