2014-08-31 56 views
2

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

在我的示例应用程序中,线程可以进行任何操作之前,集合和节点之间的连接被中断;然后在线程完成后重新连接。方法分别为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; 
     } 
    } 
} 

回答

1

一旦列表被用作ListView的内容,您只能从FX应用程序线程中操作它。有关一些使用示例,请参见Task javadocs

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

另请注意,您不应在FX应用程序线程上进行任何阻止呼叫,例如task.get(),因为您可以通过这样做来使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; 
    } 
} 
+0

我会跟您的解决方案去。复制收藏可能会成为一个问题,如果它非常大。我只能希望它永远不会发生。 – deskwarrior 2014-09-01 14:52:55

+0

还有其他选择:例如如果你只是添加东西,只需在调用方法中创建并返回新列表,然后在'onSucceeded'中执行'addAll' ... – 2014-09-01 15:15:57

相关问题