如何在 Swing 中创建延迟

Posted

技术标签:

【中文标题】如何在 Swing 中创建延迟【英文标题】:How to create a delay in 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。

【问题讨论】:

【参考方案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

以上是关于如何在 Swing 中创建延迟的主要内容,如果未能解决你的问题,请参考以下文章

Java Swing 如何在我的自定义 ColorChooserPanel 中创建颜色样本?

如何在 Java Swing 中创建投影、内发光和外发光?

如何在 Java Swing 中创建右键单击上下文菜单?

如何在 QML 中创建延迟函数?

如何在JTable Invisible for Swing Java中创建一个列

如何在Java Swing中创建响应式JList