2014-08-31 39 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

相關問題