JavaFX 一次运行大量倒计时器?

标签 java javafx timer javafx-8 fxml

所以我可以看到几种不同的方法来完成我需要的事情,并且我已经做了很多谷歌/堆栈溢出搜索,但找不到我真正想要的东西。我需要运行多个“倒计时器”。我需要在不同时间同时运行大约 6 个(可能最多 10 个)倒计时器。我的主程序上有一个选项卡 Pane ,我在其中包含 FXML 并将 Controller 注入(inject)其中。计时器选项卡具有与主程序不同的 Controller 。

所以我的第一个问题是。由于此“选项卡”运行在单独的 Controller 上,但包含在主程序中,因此它是否运行在单独的应用程序线程上?

以下是包含的选项卡 FXML 的示例...

Countdown timers

当我按下每个开始按钮时。我可以为每个计时器创建一个 TimelineKeyFrame。然而,我真的不认为这是最好的方法。特别是当您同时运行最多 10 个时间线时,并且绝对不是在与主程序不同的应用程序线程上运行。

我考虑过将每个启动请求发送到 ExecutorServicenewCacheThreadPool 但是我需要能够使用当前剩余时间更新 GUI 上的标签,我明白您不应该对后台服务执行此操作。也许是Platform.runLater()?

另一个想法是使用java.util.Timer类中的Timer。但是,当我需要更新 GUI 标签时,我发现它与 ExecutorService 具有相同的问题。我还了解到 Timer 类仅创建一个线程并按顺序执行其任务。所以,这是行不通的。

或者,我应该有一个完整的其他“CountDown”类,我可以为每个类创建新实例,然后在其中启动新线程。但是,如果我这样做,我如何不断更新 GUI。我仍然需要使用时间轴轮询 CountDown 类,对吗?这样就违背了整件事的目的。

最佳答案

So the 1st question I have is. Since this "tab" is running on a separate controller but is included into the main program, does it run on a separate application thread?

不,每个 JVM 只能有一个 JavaFX 应用程序实例,并且每个 JVM 也只能有一个 JavaFX 应用程序线程。

至于如何更新计时器,可以使用Timeline - 每个计时器一个。 Timeline 不在单独的线程上运行 - 它由负责定期更新 JavaFX GUI 的底层“场景图渲染脉冲”触发。拥有更多的 Timeline 实例基本上意味着有更多的监听器订阅“pulse”事件。

public class TimerController {
    private final Timeline timer;

    private final ObjectProperty<java.time.Duration> timeLeft;

    @FXML private Label timeLabel;

    public TimerController() {
        timer = new Timeline();
        timer.getKeyFrames().add(new KeyFrame(Duration.seconds(1), ae -> updateTimer()));
        timer.setCycleCount(Timeline.INDEFINITE);

        timeLeft = new SimpleObjectProperty<>();
    }
    public void initialize() {
        timeLabel.textProperty().bind(Bindings.createStringBinding(() -> getTimeStringFromDuration(timeLeft.get()), timeLeft));
    }

    @FXML
    private void startTimer(ActionEvent ae) {
        timeLeft.set(Duration.ofMinutes(5)); // For example timer of 5 minutes
        timer.playFromStart();
    }

    private void updateTimer() {
        timeLeft.set(timeLeft.get().minusSeconds(1));
    }

    private static String getTimeStringFromDuration(Duration duration) {
        // Do the conversion here...
    }
}

当然,您也可以使用Executor和其他线程方法,前提是您通过Platform.runLater()更新Label。或者,您可以使用任务

这是使用后台线程时的一般示例:

final Duration countdownDuration = Duration.ofSeconds(5);
Thread timer = new Thread(() -> {
    LocalTime start = LocalTime.now();
    LocalTime current = LocalTime.now();
    LocalTime end = start.plus(countDownDuration);

    while (end.isAfter(current)) {
        current = LocalTime.now();
        final Duration elapsed = Duration.between(current, end);

        Platform.runLater(() -> timeLeft.set(current)); // As the label is bound to timeLeft, this line must be inside Platform.runLater()
        Thread.sleep(1000);
    }
});

关于JavaFX 一次运行大量倒计时器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53587355/

相关文章:

java - Rxjava Android如何使用Zip运算符

JavaFX:在一行中填充了一个条目后的上下文菜单?

php - MySQL定时器和删除函数

java - Android boolean 定时器时间表

java - Try block 在 printStackTrace 方法正在打印时开始打印

java - 一个元素的流的最佳实践是什么?

Java Swing/Graphics - repaint() 或 validate() 不起作用?

css - 如何删除 'WARNING: CSS Error parsing' :

JavaFX 按钮不随 Windows 字体大小设置缩放

java.util.Timer - 如何正确使用?