简体   繁体   English

如何正确处理TextField绑定并使nodereference与Task分开

[英]How to properly handle a TextField binding and keep the nodereference separate from the Task

My question applies to a broader range of nodes than just TextField, but my example includes only this specific type of node. 我的问题不仅适用于TextField,而且适用于更广泛的节点范围,但是我的示例仅包括这种特定类型的节点。

My application changes the content of a TextField which displays the name of a country based on the selection of a city made from a ChoiceBox: 我的应用程序更改了TextField的内容,该文本字段基于ChoiceBox做出的城市选择来显示国家名称:

我的应用程序更改了TextField的内容,该文本字段基于ChoiceBox做出的城市选择来显示国家名称

I accomplish this by setting the text of countryTextField in a Task<Void> using the onAction event handler for the choice box: 我通过使用选择框的onAction事件处理程序在Task<Void>设置countryTextField的文本来完成此操作:

@FXML
public void handleCityChoice() {
    new Thread(new CityChoiceTask(cityChoiceBox.getValue())).start();
}

class CityChoiceTask extends Task<Void> {
    String selection;
    CityChoiceTask(String selection) {
        this.selection = selection;
    }
    @Override
    public Void call() {
        Platform.runLater(() -> countryTextField.setText(Datasource.getInstance().
                getCountryNameByCityName(selection)));
        return null;
    }
}

It works just fine, but really what I want to do is completely separate the node referencing from the Task code because it is a best practice to do so (I'm just a student, so correct me if I'm wrong about this, but it's what I was taught). 效果很好,但实际上我要做的是将引用的节点与Task代码完全分开,因为这样做是最佳做法(我只是一个学生,如果对此有误,请纠正我,但这就是我所教的内容。 I tried the following, but keep throwing a NullPointerException , I believe it is because the task thread doesn't complete before the bind is executed: 我尝试了以下操作,但始终抛出NullPointerException ,我相信是因为在执行绑定之前任务线程未完成:

@FXML
public void handleCityChoice() {
    Task task = new CityChoiceTask(cityChoiceBox.getValue());
    new Thread(task).start();
    countryTextField.textProperty().bind(task.valueProperty());
}

class CityChoiceTask extends Task<SimpleStringProperty> {
    String selection;
    CityChoiceTask(String selection) {
        this.selection = selection;
    }
    @Override
    public SimpleStringProperty call() {
        String result = Datasource.getInstance().getCountryNameByCityName(selection));
        return new SimpleStringProperty(result);
    }
}

Also, System.out.println(task.valueProperty()); 另外, System.out.println(task.valueProperty());

prints: 印刷品:

ObjectProperty [bean: controller.AddCustomer$CityChoiceTask@8b85d08, name: value, value: null] ObjectProperty [bean:controller.AddCustomer$CityChoiceTask@8b85d08,名称:值,值:null]

Is the thread not executing in time indeed why it's throwing a NullPointerException , and is there a way to fix it? 线程是否未及时执行确实是为什么它抛出NullPointerException ,并且有一种解决方法?

On a side note, this sort of bind works just fine when using TableView. 附带说明一下,这种绑定在使用TableView时很好用。 For instance, using a Task that returns a ValueProperty of FXCollections.observableArrayList , something like myTableView.itemsProperty().bind(task); 例如,使用一个返回FXCollections.observableArrayList的ValueProperty的FXCollections.observableArrayList ,例如FXCollections.observableArrayList myTableView.itemsProperty().bind(task); FXCollections.observableArrayList works just fine, regardless of when the thread finished execution. 无论线程何时完成执行,工作都很好。

You need to use the proper return type as type parameter to the Task . 您需要使用正确的返回类型作为Task类型参数。 Since you want to assign a String Property, you need to use String . 由于要分配String属性,因此需要使用String (Your compiler would have complained about this error, if you didn't use the raw type.) (如果您不使用原始类型,则您的编译器会抱怨此错误。)

@Override
public void start(Stage primaryStage) {
    TextField text = new TextField();

    Button btn = new Button("Run");
    btn.setOnAction((ActionEvent event) -> {
        Task<String> task = new Task<String>() {

            @Override
            protected String call() throws Exception {
                Thread.sleep(1000);
                return "Hello World";
            }

        };
        text.textProperty().bind(task.valueProperty());
        new Thread(task).start();
    });

    VBox root = new VBox(btn, text);

    Scene scene = new Scene(root);

    primaryStage.setScene(scene);
    primaryStage.show();
}

You could also post intermediate updates using updateValue method. 您也可以使用updateValue方法发布中间更新。


Also note that your first snippet does not really do any work on the non-application thread. 还要注意,您的第一个代码段实际上并未在非应用程序线程上做任何工作。 The task is only used to schedule the code for retrieving the information later on the application thread. 该任务仅用于计划代码,以便稍后在应用程序线程上检索信息。 Also using Task for this purpose is unnecessary. 也不需要为此目的使用Task A simple Runnable would suffice: 一个简单的Runnable就足够了:

final String selection = cityChoiceBox.getValue();
new Thread(() ->  {
    // do this on this thread, not on the JavaFX application thread
    final String result = Datasource.getInstance().getCountryNameByCityName(selection);

    Platform.runLater(() -> countryTextField.setText(result));
}).start();

You should change type of your task to String and call updateValue() from the call() method of your task: 您应该将任务的类型更改为String然后从任务的call()方法调用updateValue()

class CityChoiceTask extends Task<String> {

    private final String selection;

    CityChoiceTask(String selection) {
        this.selection = selection;
    }

    @Override
    public String call() {
        String result = Datasource.getInstance().getCountryNameByCityName(selection);
        updateValue(result);
        return result;
    }
}

And in your handleCityChoice() method you should bind countryTextField.textProperty() prior to starting thread as it can finish execution before the next line of code (creating the binding) will be executed, making your countryText field be never updated: 而在你handleCityChoice()方法,你应该绑定countryTextField.textProperty()之前,要启动线程,因为它可以在下一行代码前完成执行(创建绑定)将被执行,使您的countryText现场从不更新:

@FXML
public void handleCityChoice() {
    CityChoiceTask task = new CityChoiceTask(cityChoiceBox.getValue());
    countryTextField.textProperty().bind(task.valueProperty());
    new Thread(task).start();

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM