JavaFX 中的计时器更新标签(绑定)
Timer in JavaFX updating Label (Bindings)
我想在我的应用程序中添加一个时钟,告诉您完成任务的时间。为了简化它,我在新线程中包含了一个每秒递增的计数器,并使用计数器编号更新标签 'setTimer'。为此,我在我的 .fxml 文件中有一个标签 fx:id="setTimer" 并将其导入到我的 class.
@FXML
private Label setTimer;
并在我的 class 中创建了另一个 class,它扩展线程 TimerTask 并在每次调用时将计数器递增 1。创建了一个新对象 'text',应始终使用计数器的当前值进行更新。
SimpleStringProperty text = new SimpleStringProperty("undefined");
public class MyTask extends TimerTask {
@Override
public void run() {
counter++;
text.set(Integer.toString(counter));
}
}
为了让这个 class 每秒调用一次,我在初始化方法中创建了一个计时器并将其设置为一秒。
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
MyTask myTask = new MyTask();
Timer timer = new Timer(true);
timer.scheduleAtFixedRate(myTask, 0 , 1000);
setTimer.textProperty().bind(text);
}
目前我得到异常 'Not on FX application thread; currentThread = Timer-0'。
我已经尝试了很多方法来解决我的问题,但我没有找到正确的方法。
我想做什么我的想法应该很清楚,如果有人能帮助我,我会很高兴。
我的问题是更新 GUI 中计数器的更改。
它不必像我想象的那样解决,只需要一个关于如何最好地实现它的提示。
谢谢
好吧,我的评论太长了。这就是我尝试做的方式。
- 在正在加载的应用程序上启动秒表
- 创建一个每隔一段时间自行启动的新线程。
- 在那里,以秒为单位从秒表获取时间 (
sw.getTime(TimeUntis.seconds)
)。如果需要 like shown in this SO post ,请将其转换为小时和分钟
- 然后,使用
Platform.runLater(new Runnable(){ /* access ui element and write time here */ });
将时间写入UI
在后台线程中使用 Platform.runLater() 有点混乱,应该避免。 JavaFX 有处理这种事情的机制,您应该使用它。具体来说,Task<> 旨在允许后台线程更新连接到需要在 FXAT 上更新的 JavaFX 屏幕元素的数据。
你可以用 JavaFX 任务做你想做的事,但是在里面使用 Java 定时器似乎是不可能的,因为似乎没有任何办法Java 线程等待 Timer 完成。所以,相反,我使用了一个带有睡眠的“for”循环来做同样的事情。它很笨拙,但它确实演示了如何将任务的部分结果连接到屏幕显示:
public class Sample1 extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
Scene scene = new Scene(new Timer1(), 300, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
}
public class Timer1 extends VBox {
public Timer1() {
Text time = new Text();
Button startButton = new Button("Start");
Button stopButton = new Button("Stop");
getChildren().addAll(time, startButton, stopButton);
startButton.setOnAction(startEvt -> {
Task<Integer> timerFxTask = new Task<>() {
{
updateValue(0);
}
@Override
protected Integer call() throws Exception {
for (int counter = 0; counter <= 1000; counter++) {
sleep(1000);
updateValue(counter);
}
return 1000;
}
};
stopButton.setOnAction(stopEvt -> timerFxTask.cancel());
time.textProperty().bind(Bindings.createStringBinding(() -> timerFxTask.getValue().toString(),
timerFxTask.valueProperty()));
Thread timerThread = new Thread(timerFxTask);
timerThread.start();
});
}
}
但是有一种更好的方法来完成您想要做的事情,它本质上是一个动画 - JavaFX 有一个工具可以做到这一点。通常,人们使用动画来改变 JavaFX 屏幕元素的外观,但您也可以使用它来随着时间的推移为文本的内容设置动画。我在这里所做的是创建一个 IntegerProperty,它可以从起始值过渡到随时间线性插值的结束值,然后将该值绑定到屏幕上文本的 TextProperty。所以你看到它每秒更新一次。
public class Sample1 extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
Scene scene = new Scene(new Timer2(), 300, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
}
public class Timer2 extends VBox {
public Timer2() {
Text time = new Text();
Button startButton = new Button("Start");
Button stopButton = new Button("Stop");
getChildren().addAll(time, startButton, stopButton);
startButton.setOnAction(startEvt -> {
IntegerProperty counter = new SimpleIntegerProperty(0);
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1000), new KeyValue(counter, 1000)));
stopButton.setOnAction(stopEvt -> timeline.stop());
time.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(counter.get()), counter));
timeline.play();
});
}
}
我想在我的应用程序中添加一个时钟,告诉您完成任务的时间。为了简化它,我在新线程中包含了一个每秒递增的计数器,并使用计数器编号更新标签 'setTimer'。为此,我在我的 .fxml 文件中有一个标签 fx:id="setTimer" 并将其导入到我的 class.
@FXML
private Label setTimer;
并在我的 class 中创建了另一个 class,它扩展线程 TimerTask 并在每次调用时将计数器递增 1。创建了一个新对象 'text',应始终使用计数器的当前值进行更新。
SimpleStringProperty text = new SimpleStringProperty("undefined");
public class MyTask extends TimerTask {
@Override
public void run() {
counter++;
text.set(Integer.toString(counter));
}
}
为了让这个 class 每秒调用一次,我在初始化方法中创建了一个计时器并将其设置为一秒。
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
MyTask myTask = new MyTask();
Timer timer = new Timer(true);
timer.scheduleAtFixedRate(myTask, 0 , 1000);
setTimer.textProperty().bind(text);
}
目前我得到异常 'Not on FX application thread; currentThread = Timer-0'。
我已经尝试了很多方法来解决我的问题,但我没有找到正确的方法。 我想做什么我的想法应该很清楚,如果有人能帮助我,我会很高兴。 我的问题是更新 GUI 中计数器的更改。 它不必像我想象的那样解决,只需要一个关于如何最好地实现它的提示。
谢谢
好吧,我的评论太长了。这就是我尝试做的方式。
- 在正在加载的应用程序上启动秒表
- 创建一个每隔一段时间自行启动的新线程。
- 在那里,以秒为单位从秒表获取时间 (
sw.getTime(TimeUntis.seconds)
)。如果需要 like shown in this SO post ,请将其转换为小时和分钟
- 然后,使用
Platform.runLater(new Runnable(){ /* access ui element and write time here */ });
将时间写入UI
在后台线程中使用 Platform.runLater() 有点混乱,应该避免。 JavaFX 有处理这种事情的机制,您应该使用它。具体来说,Task<> 旨在允许后台线程更新连接到需要在 FXAT 上更新的 JavaFX 屏幕元素的数据。
你可以用 JavaFX 任务做你想做的事,但是在里面使用 Java 定时器似乎是不可能的,因为似乎没有任何办法Java 线程等待 Timer 完成。所以,相反,我使用了一个带有睡眠的“for”循环来做同样的事情。它很笨拙,但它确实演示了如何将任务的部分结果连接到屏幕显示:
public class Sample1 extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
Scene scene = new Scene(new Timer1(), 300, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
}
public class Timer1 extends VBox {
public Timer1() {
Text time = new Text();
Button startButton = new Button("Start");
Button stopButton = new Button("Stop");
getChildren().addAll(time, startButton, stopButton);
startButton.setOnAction(startEvt -> {
Task<Integer> timerFxTask = new Task<>() {
{
updateValue(0);
}
@Override
protected Integer call() throws Exception {
for (int counter = 0; counter <= 1000; counter++) {
sleep(1000);
updateValue(counter);
}
return 1000;
}
};
stopButton.setOnAction(stopEvt -> timerFxTask.cancel());
time.textProperty().bind(Bindings.createStringBinding(() -> timerFxTask.getValue().toString(),
timerFxTask.valueProperty()));
Thread timerThread = new Thread(timerFxTask);
timerThread.start();
});
}
}
但是有一种更好的方法来完成您想要做的事情,它本质上是一个动画 - JavaFX 有一个工具可以做到这一点。通常,人们使用动画来改变 JavaFX 屏幕元素的外观,但您也可以使用它来随着时间的推移为文本的内容设置动画。我在这里所做的是创建一个 IntegerProperty,它可以从起始值过渡到随时间线性插值的结束值,然后将该值绑定到屏幕上文本的 TextProperty。所以你看到它每秒更新一次。
public class Sample1 extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
Scene scene = new Scene(new Timer2(), 300, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
}
public class Timer2 extends VBox {
public Timer2() {
Text time = new Text();
Button startButton = new Button("Start");
Button stopButton = new Button("Stop");
getChildren().addAll(time, startButton, stopButton);
startButton.setOnAction(startEvt -> {
IntegerProperty counter = new SimpleIntegerProperty(0);
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1000), new KeyValue(counter, 1000)));
stopButton.setOnAction(stopEvt -> timeline.stop());
time.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(counter.get()), counter));
timeline.play();
});
}
}