问题描述
所以我可以看到几种不同的方式来做我需要的事情,我已经做了很多Google/Stack溢出搜索,但是找不到我真正想要的东西.我需要运行多个倒数计时器".我需要在不同的时间一次运行大约6个可能多达10个倒数计时器.我的主程序上有一个选项卡窗格,其中包括FXML并将控制器注入其中.计时器选项卡具有与主程序不同的控制器.
So I can see a couple different ways to do what I need and I've done a bunch of google/stack overflow searching but can't find really what I'm looking for. I need to run multiple "Countdown timers." I need to have about 6 possibly up to 10 countdown timers running at once all at different times. I have a tab pane on my main program that I am including the FXML and injecting the controllers into. The Timers Tab has a different controller than the main program.
所以我的第一个问题是.由于此选项卡"在单独的控制器上运行,但包含在主程序中,因此它是否在单独的应用程序线程上运行?
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?
以下是所包含的选项卡FXML外观的示例...
Here is an example of what the included tab FXML looks like...
当我按下每个开始按钮时.我可以为每个计时器创建一个Timeline
和KeyFrame
.但是,我并不真的认为这是最好的方法.特别是一次您最多同时运行10个时间轴,并且确定是否没有在与主程序分开的应用程序线程上运行.
When I press each start button. I can create a Timeline
and KeyFrame
for each timer. However, I don't really think that is the best way to go about it. Specially once you get up to 10 timelines running at the same time, and definitely if this is not running on a separate application thread from the main program.
我曾考虑过将每个启动请求发送到ExecutorService
和newCacheThreadPool
,但是我需要能够使用当前剩余时间更新GUI上的标签,并且我知道您不应该使用后台服务来完成此操作. Platform.runLater()
也许?
I thought about sending each start request to an ExecutorService
and newCacheThreadPool
however I need to be able to update the labels on the GUI with the current time remaining and I understand you are not supposed to do that with background services. Platform.runLater()
maybe?
另一个想法是使用java.util.Timer
类中的Timer
.但是,当我需要更新GUI标签时,我认为这与ExecutorService
存在相同的问题.我也了解Timer
类仅创建一个线程并按顺序执行其任务.所以,那是行不通的.
The other idea was using the Timer
from the java.util.Timer
class. However, I see this as having the same problems as the ExecutorService
when I need to update the GUI Labels. I also understand that the Timer
class only creates one thread and performs it's tasks sequentially. So, that wouldn't work.
或者,我是否应该有一个完整的"CountDown"类,我可以为每个类创建新的实例,然后在其中启动新线程.但是,如果这样做,我将如何不断更新GUI.我仍然需要使用timeline
来轮询CountDown类吗?因此,这将破坏整个事情的目的.
Or, should I have a whole other "CountDown" class that I can create new instances of with each one and then start new threads in. However, if I do that, how do I continually update the GUI. I still would have to poll the CountDown class using a timeline
right? So that would defeat the purpose of this whole thing.
推荐答案
不,每个JVM只能有一个 个JavaFX应用程序实例,每个JVM只能有一个一个 JavaFX应用程序线程.
No, there can only be one JavaFX Application instance per JVM, and also one JavaFX Application Thread per JVM.
关于如何更新计时器,可以使用Timeline
-每个计时器一个. Timeline
不在单独的线程上运行-它由底层的场景图呈现脉冲"触发,该脉冲负责定期更新JavaFX GUI.拥有更多的Timeline
实例基本上只是意味着有更多的侦听器订阅"pulse"事件.
As for how you could update the timer, it is fine to use Timeline
- one for each timer. Timeline
does not run on separate thread - it is triggered by the underlying "scene graph rendering pulse" which is responsible for updating the JavaFX GUI periodically. Having more Timeline
instances basically just means that there are more listeners that subscribes to the "pulse" event.
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...
}
}
当然,如果您通过Platform.runLater()
更新Label
,也可以使用Executor
和其他线程方法.或者,您可以使用Task
.
Of course, you can also use Executor
and other threading methods, provided you update the Label
via Platform.runLater()
. Alternatively, you could use a Task
.
这是使用后台线程时的一般示例:
This is a general example when using background thread:
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一次运行大量的倒数计时器吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!