ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2019-06-08 20:47:07  阅读:391  来源: 互联网

标签:java multithreading javafx-8


我有一个工作任务,它不断使用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<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);
    }
}

标签:java,multithreading,javafx-8
来源: https://codeday.me/bug/20190608/1200276.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有