为了效果,我想要一个标签来显示时间就像秒表一样。时间从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 :(得分:2)
你的时钟滞后的主要原因是你每秒1000次将时间增加0.0001秒(即1/10000秒)。所以每秒它都会增加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();
停止此操作。显然,您可以添加更多功能来设置运行时间等,如果需要,可以从stop()
方法调用handle(...)
。
SSCEE:
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帧的速度渲染时,您获得的精度远远超过您实际使用的精度。