我有一个工作任务,它不断使用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));
}
}; }
答案 0 :(得分:5)
一个解决方案(下面详细介绍)是让UI的控制器公开一个属性,并在控制器内部监听此属性的更改以更新显示。然后编排应用程序的组件将控制器的属性绑定到服务的lastValueProperty
。
鉴于以下服务:
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()
:
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;你可能想要在现实应用中更聪明的东西:
<?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>
最后是创建组件并绑定其属性的应用程序主类:
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);
}
}