如何在 Swing 中创建延迟

Posted

技术标签:

【中文标题】如何在 Swing 中创建延迟【英文标题】:How to create a delay in Swing 【发布时间】:2011-11-07 06:34:55 【问题描述】:

我做了一个二十一点游戏,我希望 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】:

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

首先,您的类中需要一个 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 很容易做到这一点。【参考方案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!为button.addActionListener( e -&gt; xyButtonClicked()); 使用 Lambdas 这样,您可以轻松分离关注点,而无需检查 Action 的来源。特别是在您想手动调用它们的情况下。 2)在您的成员变量前面加上可见性(私有),最后在任何地方都可以!如果可能,在变量声明时初始化它们。 3) myDealerComponent() 是你能给函数起的最糟糕的名字,因为它缺少 get 并且可能有一些不起眼的魔法 invo【参考方案3】:

我认为this tutorial 中明确了如何使用定时器来实现你想要的,而不必处理线程。

【讨论】:

esta respuesta es de la buena【参考方案4】:

所以我查看了 Timers,但我不明白如何使用 Timers

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

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

【讨论】:

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

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

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

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

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

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

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

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