# 如何使用摆动计时器

> 原文： [https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html](https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html)

Swing 计时器（ [`javax.swing.Timer`](https://docs.oracle.com/javase/8/docs/api/javax/swing/Timer.html) 的实例）在指定的延迟后触发一个或多个动作事件。不要将 Swing 计时器与`java.util`包中的通用计时器设施混淆。本页仅介绍 Swing 计时器。

通常，我们建议使用 Swing 计时器而不是通用计时器来处理与 GUI 相关的任务，因为 Swing 计时器都共享相同的预先存在的计时器线程，并且 GUI 相关任务会自动在事件派发线程上执行。但是，如果您不打算从计时器触摸 GUI，或者需要执行冗长的处理，则可以使用通用计时器。

您可以通过两种方式使用 Swing 计时器：

*   在延迟之后执行一次任务。
    例如，工具提示管理器使用 Swing 计时器来确定何时显示工具提示以及何时隐藏它。
*   反复执行任务。
    例如，您可以执行动画或更新显示目标进度的组件。

摆动定时器非常易于使用。创建计时器时，指定一个动作侦听器，以便在计时器“熄灭”时收到通知。此侦听器中的`actionPerformed`方法应包含您需要执行的任何任务的代码。创建计时器时，还可以指定计时器启动之间的毫秒数。如果您希望计时器仅关闭一次，则可以在计时器上调用`setRepeats(false)`。要启动计时器，请调用其`start`方法。要暂停它，请拨打`stop`。

请注意，Swing 计时器的任务是在事件派发线程中执行的。这意味着任务可以安全地操作组件，但这也意味着任务应该快速执行。如果任务可能需要一段时间才能执行，那么考虑使用`SwingWorker`代替计时器或者除了计时器之外还使用`SwingWorker`。有关使用`SwingWorker`类的说明和有关在多线程程序中使用 Swing 组件的信息，请参阅 Swing 中的[并发。](../concurrency/index.html)

让我们看一个使用计时器定期更新组件的示例。 [``TumbleItem`` ](../examples/components/TumbleItemProject/src/components/TumbleItem.java)小程序使用计时器定期更新其显示。 （要查看此 applet 正在运行，请转到[如何创建 Applet](../components/applet.html) 。此 applet 首先创建并启动计时器：

```
timer = new Timer(speed, this);
timer.setInitialDelay(pause);
timer.start(); 

```

`speed`和`pause`变量代表 applet 参数;如在另一页上配置的那样，它们分别是 100 和 1900，因此第一个定时器事件将在大约 1.9 秒内发生，并且每 0.1 秒重复一次。通过将`this`指定为`Timer`构造函数的第二个参数，`TumbleItem`指定它是计时器事件的动作侦听器。

启动计时器后，`TumbleItem`开始在后台线程中加载一系列图像。同时，计时器事件开始发生，导致`actionPerformed`方法执行：

```
public void actionPerformed(ActionEvent e) {
    //If still loading, can't animate.
    if (!worker.isDone()) {
        return;
    }

    loopslot++;

    if (loopslot >= nimgs) {
        loopslot = 0;
        off += offset;

        if (off < 0) {
            off = width - maxWidth;
        } else if (off + maxWidth > width) {
            off = 0;
        }
    }

    animator.repaint();

    if (loopslot == nimgs - 1) {
        timer.restart();
    }
}

```

在加载图像之前，`worker.isDone`返回`false`，因此有效地忽略了定时器事件。事件处理代码的第一部分只是设置动画控件的`paintComponent`方法中使用的值：`loopslot`（动画中下一个图形的索引）和`off`（下一个图形的水平偏移） 。

最终，`loopslot`将到达图像阵列的末尾并重新开始。发生这种情况时，`actionPerformed`末尾的代码重新启动计时器。这样做会在动画序列再次开始之前导致短暂的延迟。