为了达到效果,我想要一个像秒表一样显示时间的标签。时间从 0 开始,到 2 或 3 秒左右结束。该服务在需要时停止。我遇到的问题是因为我试图每秒更新文本 1000 次,计时器滞后了。
就像我说的,这是为了效果,一旦计时器停止就会隐藏,但时间应该相当准确,而不是落后 3 秒。
有什么方法可以让它更快吗?如果可能的话,我希望保留所有 4 位小数。
timer = new Label();
DoubleProperty time = new SimpleDoubleProperty(0.0);
timer.textProperty().bind(Bindings.createStringBinding(() -> {
return MessageFormat.format(gui.getResourceBundle().getString("ui.gui.derbydisplay.timer"), time.get());
}, time));
Service<Void> countUpService = new Service<Void>() {
@Override
protected Task<Void> createTask() {
return new Task<Void>() {
@Override
protected Void call() throws Exception {
while (!isCancelled()) {
Platform.runLater(() -> time.set(time.get() + 0.0001));
Thread.sleep(1);
}
return null;
}
};
}
};
最佳答案
您的时钟滞后的主要原因是因为您每秒将时间增加 0.0001 秒(即 1/10000 秒)1000 次。所以每秒它会增加 0.1...但是即使你解决了这个问题,它仍然会有少量滞后,因为你没有考虑进行方法调用所花费的时间。您可以通过在启动时检查系统时钟来解决此问题,然后在每次进行更新时检查它。
每秒更新标签 1000 次几乎是多余的,因为 JavaFX 的目的只是每秒更新 UI 60 次。 (如果 UI 线程忙于尝试做太多事情,它会变慢,您也可以通过安排对 Platform.runLater()
的多次调用来实现这种情况。)您可以通过使用AnimationTimer
. AnimationTimer.handle(...)
方法在每次渲染帧时被调用一次,所以这有效地更新了 JavaFX 允许 UI 更新的频率。
AnimationTimer timer = new AnimationTimer() {
private long startTime ;
@Override
public void start() {
startTime = System.currentTimeMillis();
super.start();
}
@Override
public void handle(long timestamp) {
long now = System.currentTimeMillis();
time.set((now - startTime) / 1000.0);
}
};
您可以使用 timer.start();
启动它并使用 timer.stop();
停止它。显然,您可以添加更多功能来设置运行时间等,并在需要时从 handle(...)
方法调用 stop()
。
上海商会:
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Timer extends Application {
@Override
public void start(Stage primaryStage) {
Label label = new Label();
DoubleProperty time = new SimpleDoubleProperty();
label.textProperty().bind(time.asString("%.3f seconds"));
BooleanProperty running = new SimpleBooleanProperty();
AnimationTimer timer = new AnimationTimer() {
private long startTime ;
@Override
public void start() {
startTime = System.currentTimeMillis();
running.set(true);
super.start();
}
@Override
public void stop() {
running.set(false);
super.stop();
}
@Override
public void handle(long timestamp) {
long now = System.currentTimeMillis();
time.set((now - startTime) / 1000.0);
}
};
Button startStop = new Button();
startStop.textProperty().bind(
Bindings.when(running)
.then("Stop")
.otherwise("Start"));
startStop.setOnAction(e -> {
if (running.get()) {
timer.stop();
} else {
timer.start();
}
});
VBox root = new VBox(10, label, startStop);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 320, 120);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
传递给handle(...)
方法的值实际上是以纳秒为单位的时间戳。您可以使用它并将 startTime
设置为 System.nanoTime()
,尽管您获得的精度远远超过帧渲染时实际使用的精度每秒 60 帧。
关于java - JavaFX 中的快速计数计时器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34801227/