multithreading - 在线程中更改 Observable 集合(绑定(bind)到 JavaFX 节点)

标签 multithreading collections binding javafx observable

在线程中操作 Observable 集合的正确方法是什么,其中集合已经绑定(bind)到 JavaFX UI 节点?

在我的示例应用程序中,集合和节点之间的连接在线程可以进行任何操作之前就断开了;然后在线程完成后重新连接它们。方法是disconnectObservable()connectObservable()分别。如果没有这两种方法,java.lang.IllegalStateException: Not on FX application thread被报道。

理想情况下,我希望 ChangeObservableTask 对 mWords 进行更改, 然后我会调用一些方法告诉 mObservable刷新自身并通知其监听器。有这样的事吗?

谢谢。

package theapp;

import java.util.LinkedList;
import java.util.List;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ThreadObList extends Application {
    private final List<String> mWords;
    private final ObservableList<String> mObservable;
    private ListView mListView;
    private Label mCount;

    public ThreadObList() {
        mWords = new LinkedList<>();
        mObservable = FXCollections.observableList(mWords);
        mWords.add("park");
    }

    @Override
    public void start(Stage primaryStage) {
        Button btn = new Button();
        btn.setText("Start thread");
        btn.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                ChangeObservableTask task = new ChangeObservableTask();
                Thread thd = new Thread(task);
                disconnectObservable();
                thd.start();
                try {
                    task.get();
                    System.out.println("ChangeObservableTask exited normally.");
                } 

                catch(Exception ex) {
                    System.out.println(ex.getMessage());
                }
                connectObservable();
            }
        });

        mCount = new Label();
        mListView = new ListView();
        VBox root = new VBox(5, btn, mCount, mListView);
        VBox.setVgrow(mListView, Priority.ALWAYS);
        connectObservable();

        Scene scene = new Scene(root, 300, 250);
        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

    private void connectObservable() {
        mListView.setItems(mObservable);
        mCount.textProperty().bind(Bindings.size(mObservable).asString());
    }

    private void disconnectObservable() {
        mListView.setItems(null);
        mCount.textProperty().unbind();
    }

    private class ChangeObservableTask extends Task<Void> {
        @Override
        protected Void call() throws Exception {
            mObservable.add("dart");
            mObservable.add("truck");
            mObservable.add("ocean");
            return null;
        }
    }
}

最佳答案

一旦列表被用作 ListView 的内容,您就只能从 FX 应用程序线程中对其进行操作。见 Task javadocs一堆使用示例。

您可以创建您的 ObservableList 的副本并将其传递给您的任务,操作副本并返回结果。然后更新 ObservableList结果在onSucceeded处理程序。

另请注意,您不应进行任何阻塞调用,例如 task.get()在 FX 应用程序线程上,因为您可以通过这样做使 UI 无响应。

所以你应该按照以下方式做一些事情:

   btn.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent event) {
            ChangeObservableTask task = new ChangeObservableTask(new ArrayList<>(mObservable));
            Thread thd = new Thread(task);
            task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
                @Override
                public void handle(WorkerStateEvent event) {
                    mObservable.setAll(task.getValue());
                }
            });
            thd.start();

        }
    });


private class ChangeObservableTask extends Task<List<String>> {
    private final List<String> data ;

    ChangeObservableTask(List<String> data) {
        this.data = data ;
    }

    @Override
    protected List<String> call() throws Exception {
        data.add("dart");
        data.add("truck");
        data.add("ocean");
        return data;
    }
}

关于multithreading - 在线程中更改 Observable 集合(绑定(bind)到 JavaFX 节点),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25594742/

相关文章:

c++ - 有没有更好的方法使这段代码线程安全? Thread_local static 似乎是一个生硬的工具

python - 槽在哪个线程中执行,我可以将其重定向到另一个线程吗?

java - 用于内存高效数据存储的 HashMap 替代方案

c# - Oracle.DataAccess (ODP.NET) 数组绑定(bind) "Value does not fall within the expected range"

multithreading - 检查输入端口上的数据是否可用

java - 在Thread算法工作期间重绘JPanel

wpf - 绑定(bind)模式效率

c# - 为什么我的 ConvertBack 没有被调用,WPF 转换器和 ValidationRule?

c# - 将一个项目多次添加到同一个列表

forms - 如何在 Symfony 2 表单中自定义 data-prototype 属性