在线程中操作 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/