[英]Javafx: Label turns blank if service message updates(Label binding to msg)
I am new to java as well as javaFx, I am trying to work on a project which needs to display some live incoming data on labels. 我是java和javaFx的新手,我正在尝试处理需要在标签上显示一些实时传入数据的项目。
I bind my label to message of a service object which keep updating its message with the incoming data. 我将我的标签绑定到服务对象的消息,该服务对象继续使用传入数据更新其消息。 However, the message is updating but the label turns to blank. 但是,消息正在更新,但标签变为空白。
There are no errors pop up nor exceptions been caught. 弹出没有错误,也没有捕获异常。 Can anyone point out what makes the label blank rather than updating along with the service.message and how to fix it? 任何人都可以指出是什么使标签空白而不是与service.message一起更新以及如何修复它? Thanks in advance. 提前致谢。
Below is a simplified example of what I am trying to do, and in which the data source is replaced by a list of random numbers. 下面是我尝试做的简化示例,其中数据源被随机数列表替换。
controller class: where I put the scheduledservice object in, and binding it to the label. controller类:我将scheduledservice对象放入其中,并将其绑定到标签。
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.util.Duration;
public class Controller {
@FXML
Button startButton;
@FXML
Label updateLabel;
@FXML
void display(ActionEvent event) throws Exception{
Steaming steaming = new Steaming();
ScheduledService<Void> service = new ScheduledService<Void>() {
protected Task<Void> createTask() {
return new Task<Void>() {
protected Void call() {
// Call the method and update the message
updateMessage(steaming.processData());
return null; // Useful in case you want to return data, else null
}
};
}
};
service.setPeriod(Duration.seconds(1)); //Runs every 1 seconds
service.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent t) {
System.out.println("Message:" + service.messageProperty());
}
});
updateLabel.textProperty().bind(service.messageProperty());
service.start();
}
}
Streaming class contains a list, in which i generates 1000 random ints. 流类包含一个列表,其中我生成1000个随机整数。 the processData method will return and remove the 1st element in list. processData方法将返回并删除列表中的第一个元素。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Steaming{
List<Integer> numbers;
Steaming()
{
numbers = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
numbers.add(i);
}
Collections.shuffle(numbers);
}
public String processData (){
String s = "loading";
try {
s = this.numbers.get(0).toString();
numbers.remove(0);
} catch (Exception e) {
e.printStackTrace();
}
return s;
}
}
Main class, show the primary stage 主要课程,展示初级阶段
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("/sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
FXML file FXML文件
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<GridPane alignment="center" hgap="10" vgap="10" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<columnConstraints>
<ColumnConstraints />
</columnConstraints>
<rowConstraints>
<RowConstraints />
</rowConstraints>
<children>
<HBox prefHeight="100.0" prefWidth="200.0">
<children>
<Button fx:id="startButton" mnemonicParsing="false" onAction="#display" text="Button">
<HBox.margin>
<Insets right="5.0" />
</HBox.margin>
</Button>
<Label fx:id="updateLabel" prefHeight="26.0" prefWidth="105.0" text="loading">
<HBox.margin>
<Insets left="10.0" />
</HBox.margin></Label>
</children>
</HBox>
</children>
</GridPane>
Running results: Here are two Pics for both before service start and after. 运行结果:在服务开始之前和之后都有两个Pics。
Label shows "loading" before clicking the button 标签在单击按钮之前显示“正在加载”
Label shows nothing after clicking the button 单击按钮后,标签不显示任何内容
here's a part of the terminal result got from the program. 这是从程序中获得的终端结果的一部分。
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 33]
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 200]
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 389]
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 188]
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 915]
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 76]
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 205]
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 583]
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 181]
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 872]
I am new in this community as well, if there are anything that I done wrongly in the post or breach any of the rules here, please kindly point them out as well. 我也是这个社区的新手,如果我在帖子中做错任何事情或违反任何规则,请同样指出它们。 Thanks a lot! 非常感谢!
When a ScheduledService
succeeds it reschedules itself for the next execution cycle. 当ScheduledService
成功时,它会为下一个执行周期重新安排自己。 Part of this process is "resetting" some of the properties to their default values, including setting the message
property to ""
1 . 此过程的一部分是将某些属性“重置”为其默认值,包括将message
属性设置为""
1 。 So what's happening is your service succeeds, restarts, and the message is set back to ""
too quickly for it to render in the Label
. 所以正在发生的事情是你的服务成功,重新启动,并且消息被设置回""
太快,无法在Label
呈现。
Since you're code does nothing but update the message, one solution is to return the result of streaming.processData()
instead. 由于您的代码只是更新消息,因此一种解决方案是返回streaming.processData()
的结果。 You would then bind to the lastValue
property of the ScheduledService
. 然后,您将绑定到ScheduledService
的lastValue
属性。 It must be lastValue
instead of value
because the latter also gets cleared 2 on a reset/restart. 它必须是lastValue
而不是value
因为后者在重置/重启时也会被清除2 。
ScheduledService<String> service = new ScheduledService<>() {
@Override
protected Task<String> createTask() {
return new Task<>() {
@Override
protected String call() throws Exception {
return streaming.processData();
}
};
}
};
updateLabel.textProperty().bind(service.lastValueProperty());
1. I was unable to find documentation about this, but the implementation of Service.reset()
shows this to be the case ( ScheduledService
extends Service
). 1.我无法找到有关此文档的文档,但Service.reset()
的实现显示了这种情况( ScheduledService
extends Service
)。 Even if reset()
didn't clear the properties, the process of starting the service includes binding the properties to the underlying Task
—which is newly created and doesn't have any of its properties set. 即使reset()
没有清除属性,启动服务的过程也包括将属性绑定到新创建的基础Task
,并且没有设置任何属性。
2. This behavior is documented . 2.此行为记录在案 。
Adding to Slaw's answer : 添加到Slaw的答案 :
Would using JavaFx
animation tools work for you ? 使用JavaFx
动画工具会为你工作吗?
Steaming steaming = new Steaming();
Timeline timeline = new Timeline(
new KeyFrame(Duration.millis(1000),
t -> updateLabel.setText(steaming.processData()))
);
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
If steaming.processData()
is long and you want to keep it on a background thread, you can update gui like so: 如果steaming.processData()
很长并且你想把它保存在后台线程上,你可以像这样更新gui:
Steaming steaming = new Steaming();
ScheduledService<Void> service = new ScheduledService<>() {
@Override
protected Task<Void> createTask() {
return new Task<>() {
@Override
protected Void call() {
String text = steaming.processData();
Platform.runLater(()-> updateLabel.setText(text));
return null;
}
};
}
};
service.setPeriod(Duration.seconds(1)); //Runs every 1 seconds
service.start();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.