将
Runnable传递给
Platform#runLater(Runnable)包含一个无限循环。这意味着您在 JavaFX Application
Thread
上执行了无限循环,这就是UI变得无响应的原因。如果FX线程不能自由执行其工作,则无法处理用户生成的事件,并且无法安排渲染“脉冲”。后一点就是为什么即使您
setText(...)不断调用UI也不更新。
如果要继续使用当前方法,解决方法是
for(;;)从
Runnable实现中删除循环。您将设置为
TimerTask每毫秒执行一次,这意味着您要做的就是计算新状态并为每次执行设置一次标签。换句话说,该
run()方法已经“循环”。例如:
TimerTask task = new TimerTask() { @Override public void run() { Platform.runLater(() -> { // calculate new state... // update labels... // return (no loop!) }); }};也就是说,没有理由为此使用后台线程。我建议改用JavaFX提供的动画API。它是异步的,但在FX线程上执行,因此更易于实现和推理-
使用多个线程总是更复杂。要执行与当前操作类似的操作,可以使用
Timeline或
PauseTransition代替
java.util.Timer。在JavaFX的周期性后台任务
Q&A给出了使用动画用于此目的的一些很好的例子。
就个人而言,我将使用
AnimationTimer来实现秒表。这是一个例子:
import java.util.concurrent.TimeUnit;import javafx.animation.AnimationTimer;import javafx.beans.property.ReadOnlyBooleanProperty;import javafx.beans.property.ReadOnlyBooleanWrapper;import javafx.beans.property.ReadOnlyLongProperty;import javafx.beans.property.ReadOnlyLongWrapper;public class Stopwatch { private static long toMillis(long nanos) { return TimeUnit.NANOSECONDS.toMillis(nanos); } // value is in milliseconds private final ReadonlyLongWrapper elapsedTime = new ReadonlyLongWrapper(this, "elapsedTime"); private void setElapsedTime(long elapsedTime) { this.elapsedTime.set(elapsedTime); } public final long getElapsedTime() { return elapsedTime.get(); } public final ReadonlyLongProperty elapsedTimeProperty() { return elapsedTime.getReadonlyProperty(); } private final ReadonlyBooleanWrapper running = new ReadonlyBooleanWrapper(this, "running"); private void setRunning(boolean running) { this.running.set(running); } public final boolean isRunning() { return running.get(); } public final ReadonlyBooleanProperty runningProperty() { return running.getReadonlyProperty(); } private final Timer timer = new Timer(); public void start() { if (!isRunning()) { timer.start(); setRunning(true); } } public void stop() { if (isRunning()) { timer.pause(); setRunning(false); } } public void reset() { timer.stopAndReset(); setElapsedTime(0); setRunning(false); } private class Timer extends AnimationTimer { private long originTime = Long.MIN_VALUE; private long pauseTime = Long.MIN_VALUE; private boolean pausing; @Override public void handle(long now) { if (pausing) { pauseTime = toMillis(now); pausing = false; stop(); } else { if (originTime == Long.MIN_VALUE) { originTime = toMillis(now); } else if (pauseTime != Long.MIN_VALUE) { originTime += toMillis(now) - pauseTime; pauseTime = Long.MIN_VALUE; } setElapsedTime(toMillis(now) - originTime); } } @Override public void start() { pausing = false; super.start(); } void pause() { if (originTime != Long.MIN_VALUE) { pausing = true; } else { stop(); } } void stopAndReset() { stop(); originTime = Long.MIN_VALUE; pauseTime = Long.MIN_VALUE; pausing = false; } }}警告: 在AnimationTimer
运行Stopwatch
实例时,无法进行垃圾回收。
上面提供了一个属性,
elapsedTime表示经过的时间(以毫秒为单位)。根据该值,您可以计算自秒表启动以来经过的天,小时,分钟,秒和毫秒的数量。您只需收听属性并在属性更改时更新UI。



