JavaFX:如何使用Task将值从后台线程传递到JavaFX UI线程

我有一个工作任务,它不断使用Web服务并返回整数值。

我的应用程序由不同的(30)文本框和相关的单选按钮组成。 当我单击单选按钮时,我想将背景任务中的值显示到单选按钮旁边的文本字段中。

在这种特殊情况下,如何将值从后台线程传递给JAVAFX UI线程?

我是JavaFX的新手并使用以下方式。 这是正确的方法吗?

@FXML private TabPane mainTabPane; 

在主窗口控制器中创建任务,并在主窗口控制器中运行

 myScheduledService.setPeriod(Duration.seconds(10)); myScheduledService.start(); 

并创建任务:

 private class MyScheduledService extends ScheduledService { @Override protected Task createTask() { return new Task() { @Override protected String call() throws IOException, ParseException { // calling web service here Integer data = callToMyWebService(); Tab tab = mainTabPane.getTabs().get(mainTabPane.getTabs().size() - 1); BorderPane pane = (BorderPane) tab.getContent(); HBox hBox = ((HBox) pane.getChildren().get(0)); Text operationValueDisplayText = (Text) hbox.getChildren().get(2); // displaying data in text box operationValueDisplayText.setText(String.valueOf(data)); } 

}; }

一个解决方案(在下面详细实现)是让UI的控制器公开属性,并且在控制器内部监听此属性的更改以更新显示。 然后编排应用程序的组件将控制器的属性绑定到服务的lastValueProperty


一个简单,经过测试的实现

鉴于以下服务:

 import javafx.concurrent.ScheduledService; import javafx.concurrent.Task; public class BgrdService extends ScheduledService { @Override protected Task createTask() { return new BgrdTask(); } } 

……和任务:

 import java.util.Date; import javafx.concurrent.Task; class BgrdTask extends Task { @Override protected String call() throws Exception { return new Date().toString(); } } 

我们创建UI的控制器,其中valueProperty最终将绑定到服务,并且只要服务值或选定的切换发生更改,就会调用方法updateUi()

 import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.fxml.FXML; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.control.ToggleGroup; import javafx.scene.layout.GridPane; public class MainController { @FXML private ToggleGroup myToggleGroup; @FXML private GridPane grid; @FXML private Label label; private StringProperty valueProperty = new SimpleStringProperty(""); @FXML void initialize() { valueProperty.addListener((observable, oldValue, newValue) -> { updateUi(); }); myToggleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) -> { updateUi(); }); } private void updateUi() { String displayValue = getValue(); label.setText(displayValue); int index = myToggleGroup.getToggles().indexOf(myToggleGroup.getSelectedToggle()); if( index >= 0 ) { TextField textField = (TextField) grid.getChildren().get(index); textField.setText(displayValue); } } public String getValue() { return valueProperty.get(); } public void setValue(String value) { valueProperty.set(value); } public StringProperty valueProperty() { return this.valueProperty; } } 

注意:为了简单updateUi()updateUi()的实现非常幼稚,并且基于以下FXML; 在现实生活中,你可能想要更聪明的东西:

                                            

最后是创建组件并绑定其属性的应用程序主类:

 import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; import javafx.util.Duration; public class Main extends Application { @Override public void start(Stage primaryStage) throws Exception { BgrdService bgrdService = new BgrdService(); bgrdService.setPeriod(Duration.millis(3000.0)); bgrdService.start(); FXMLLoader loader = new FXMLLoader(this.getClass().getResource("Main.fxml")); MainController mainController = new MainController(); loader.setController(mainController); Parent root = loader.load(); mainController.valueProperty().bind(bgrdService.lastValueProperty()); Scene scene = new Scene(root, 500, 400); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String... args) { launch(args); } }