JavaFX:如何使用Task将值从后台线程传递到JavaFX UI线程 [英] JavaFX : How to pass value from background thread to JavaFX UI thread with Task
问题描述
我有一个工作程序任务,该任务连续消耗Web服务并返回整数值.
I have one worker task which continuously consumes web service and returns integer value.
我的应用程序由不同的(30)文本框和相关的单选按钮组成. 当我单击单选按钮时,我想在单选按钮旁边的文本"字段中显示后台任务的值.
My Application consist of different (30) text boxes with associated radio button. When I click radio button, I want to display value from background task into Text field next to radio button.
在这种特殊情况下,如何将值从后台线程传递给JAVAFX UI线程?
How I can Pass value from background thread to JAVAFX UI thread in this particular situation?
我是JavaFX的新手,并按以下方式使用.这是正确的方法吗?
I am new to JavaFX and used following way. Is this correct approach?
@FXML
private TabPane mainTabPane;
在主窗口控制器中创建的任务,并且也在主窗口控制器中运行
Created Task in main window controller and also running in main window controller
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
.
One solution (implemented in detail below) is to have the controller of the UI expose a property and, inside the controller, listen to changes of this property to update the display. Then the component orchestrating the application binds the property of the controller to the lastValueProperty
of the service.
提供以下服务:
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
public class BgrdService extends ScheduledService<String> {
@Override protected Task<String> createTask() {
return new BgrdTask();
}
}
...和任务:
import java.util.Date;
import javafx.concurrent.Task;
class BgrdTask extends Task<String> {
@Override protected String call() throws Exception {
return new Date().toString();
}
}
我们创建UI的控制器,其中valueProperty
最终将绑定到服务,并且只要服务值或选定的切换更改时,就会调用方法updateUi()
:
We create the controller of the UI, where the valueProperty
will ultimately be bound to the service and the method updateUi()
is called whenever the service value or the selected toggle changes:
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()
的实现非常幼稚,并且基于以下FXML;您可能希望在现实生活中使用更智能的东西:
NOTE: To keep things simple, the implementation of updateUi()
is very naive and based on the following FXML; you probably want something smarter in a real-life app:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.ToggleGroup?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1">
<fx:define>
<ToggleGroup fx:id="myToggleGroup"/>
</fx:define>
<children>
<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefWidth="600.0" fx:id="grid">
<columnConstraints>
<ColumnConstraints hgrow="NEVER" minWidth="10.0" prefWidth="300.0"/>
<ColumnConstraints halignment="CENTER" hgrow="NEVER" minWidth="40.0"/>
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="NEVER"/>
</rowConstraints>
<children>
<TextField/>
<TextField GridPane.rowIndex="1"/>
<TextField GridPane.rowIndex="2"/>
<TextField GridPane.rowIndex="3"/>
<TextField GridPane.rowIndex="4"/>
<RadioButton toggleGroup="$myToggleGroup" mnemonicParsing="false" GridPane.columnIndex="1" />
<RadioButton toggleGroup="$myToggleGroup" mnemonicParsing="false" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<RadioButton toggleGroup="$myToggleGroup" mnemonicParsing="false" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<RadioButton toggleGroup="$myToggleGroup" mnemonicParsing="false" GridPane.columnIndex="1" GridPane.rowIndex="3" />
<RadioButton toggleGroup="$myToggleGroup" mnemonicParsing="false" GridPane.columnIndex="1" GridPane.rowIndex="4" />
</children>
</GridPane>
<Label fx:id="label" text="Label">
<VBox.margin>
<Insets top="30.0"/>
</VBox.margin>
</Label>
</children>
</VBox>
最后是创建组件并绑定其属性的应用程序主类:
Finally the application main class that creates the components and binds their properties:
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);
}
}
这篇关于JavaFX:如何使用Task将值从后台线程传递到JavaFX UI线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!