2015-02-02 2 views
2

Есть ли существующий способ связать содержимое ListProperty? Рассмотрим следующий пример:Есть ли способ связать содержимое ListProperty в JavaFX?

private final ListProperty<Worker<?>> workers = new SimpleListProperty<>(FXCollections.observableArrayList()); 
public ListProperty<Worker<?>> workersProperty() {return workers;} 
public ObservableList<Worker<?>> getWorkers() {return workers.get();} 
public void setWorkers(ObservableList<Worker<?>> workers) {this.workers.set(workers);} 

private final ObservableList<Worker<?>> stateWatchedWorkers = FXCollections.observableArrayList(
     new Callback<Worker<?>, Observable[]>() { 
      @Override 
      public Observable[] call(final Worker<?> param) { 
       return new Observable[]{ 
         param.stateProperty() 
       }; 
      } 
     } 
); 

То, что я хочу сделать, это связать содержание stateWatchedWorkers списке работников. Если весь список workers заменен, я хочу, чтобы stateWatchedWorkers обновлялся для соответствия содержимому нового списка. Иными словами, я хочу, чтобы список stateWatchedWorkers отображался в текущий список, удерживаемый свойством .

+0

Вместо того чтобы рабочий пример в вашем вопросе, то лучше поставить его в качестве [самостоятельного ответа] (HTTP: // stackoverflow.com/help/self-answer). – jewelsea

ответ

5

Вы можете сделать

Bindings.bindContent(stateWatchedWorkers, workers); 

(поскольку ListProperty<T> реализует ObservableList<T>, путем делегирования ObservableList методов к обернутому списку и т.д.).

Вот полный пример:

import java.util.Random; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 

import javafx.application.Application; 
import javafx.beans.Observable; 
import javafx.beans.binding.Bindings; 
import javafx.beans.property.ListProperty; 
import javafx.beans.property.SimpleListProperty; 
import javafx.collections.FXCollections; 
import javafx.collections.ListChangeListener.Change; 
import javafx.collections.ObservableList; 
import javafx.concurrent.Service; 
import javafx.concurrent.Task; 
import javafx.concurrent.Worker; 
import javafx.geometry.Insets; 
import javafx.geometry.Pos; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.control.ListCell; 
import javafx.scene.control.ListView; 
import javafx.scene.layout.BorderPane; 
import javafx.scene.layout.HBox; 
import javafx.stage.Stage; 

public class ListContentBindingExample extends Application { 

    private static final Random RNG = new Random(); 
    private static final ExecutorService exec = Executors.newFixedThreadPool(5, r -> { 
     Thread t = new Thread(r); 
     t.setDaemon(true); 
     return t ; 
    }); 

    @Override 
    public void start(Stage primaryStage) { 
     final ListProperty<Worker<?>> workers = new SimpleListProperty<>(FXCollections.observableArrayList()); 

     final ObservableList<Worker<?>> stateWatchedWorkers = FXCollections.observableArrayList(worker -> new Observable[] { worker.stateProperty() }); 
     Bindings.bindContent(stateWatchedWorkers, workers); 

     Button newWorker = new Button("New Worker") ; 
     newWorker.setOnAction(event -> workers.add(createTask())); 

     Button replaceAllWorkers = new Button("New Worker List"); 
     replaceAllWorkers.setOnAction(event -> { 
      ObservableList<Worker<?>> newWorkers = FXCollections.observableArrayList(); 
      for (int i=0; i<=10; i++) { 
       newWorkers.add(createTask()); 
      } 
      workers.set(newWorkers); 
     }); 

     ListView<Worker<?>> workerView = new ListView<>(); 
     workerView.setCellFactory(listView -> new ListCell<Worker<?>>() { 
      @Override 
      public void updateItem(Worker<?> worker, boolean empty) { 
       super.updateItem(worker, empty); 
       if (empty) { 
        setText(null); 
       } else { 
        setText(worker.getState().toString()); 
       } 
      } 
     }); 
     workerView.setItems(stateWatchedWorkers); 

     workers.get().addListener((Change<? extends Worker<?>> change) -> stateWatchedWorkers.forEach(w -> System.out.println(w.getState()))); 
     stateWatchedWorkers.addListener((Change<? extends Worker<?>> change) -> stateWatchedWorkers.forEach(w -> System.out.println(w.getState()))); 

     HBox buttons = new HBox(5, newWorker, replaceAllWorkers); 
     buttons.setAlignment(Pos.CENTER); 
     buttons.setPadding(new Insets(10)); 

     primaryStage.setScene(new Scene(new BorderPane(workerView, null, null, buttons, null), 250, 400)); 
     primaryStage.show(); 
    } 

    private Worker<Void> createTask() { 
     Service<Void> service = new Service<Void>() { 

      @Override 
      protected Task<Void> createTask() { 
       return new Task<Void>() { 
        @Override 
        public Void call() throws Exception { 
         Thread.sleep(1000 + RNG.nextInt(2000)); 
         return null ; 
        } 
       }; 
      } 

     }; 

     service.setExecutor(exec); 
     service.start(); 
     return service ; 
    } 

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

намек James_D в о ListProperty делегируя его ObservableList правильный ответ. Вот еще один пример кода, который показывает привязку содержимого ListProperty «просто работает».

import javafx.beans.Observable; 
import javafx.beans.binding.Bindings; 
import javafx.beans.property.*; 
import javafx.collections.FXCollections; 
import javafx.collections.ListChangeListener; 
import javafx.collections.ObservableList; 
import javafx.util.Callback; 

public class ListPropertyContentBinding { 
    /* This is a quick test to see how ListProperty notifies about list changes 
    * when the entire list is swapped. 
    * 
    * Conclusion: When the backing list is changed, ListProperty will perform 
    * notification indicating that all items from the original list were 
    * removed and that all items from the updated (new) list were added. 
    */ 

    ObservableList<Person> oldPeople = FXCollections.observableArrayList(); 
    ObservableList<Person> newPeople = FXCollections.observableArrayList(); 

    // A list property that is used to hold and swap the lists. 
    ListProperty<Person> people = new SimpleListProperty<>(oldPeople); 

    // A list that includes an extractor. This list will be bound to people and 
    // is expected to additionally notify whenever the name of a person in the 
    // list changes. 
    ObservableList<Person> nameWatchedPeople = FXCollections.observableArrayList(
      new Callback<Person, Observable[]>() { 
       @Override 
       public Observable[] call(final Person person) { 
        return new Observable[]{ 
          person.name 
        }; 
       } 
      }); 

    public ListPropertyContentBinding() { 
     Bindings.bindContent(nameWatchedPeople, people); 

     nameWatchedPeople.addListener((ListChangeListener<Person>) change -> { 
      while (change.next()) { 
       System.out.println(" " + change.toString()); 
      } 
     }); 

     Person john = new Person("john"); 
     System.out.println("Adding john to oldPeople. Expect 1 change."); 
     oldPeople.add(john); 

     System.out.println("Changing john's name to joe. Expect 1 change."); 
     john.name.set("joe"); 

     Person jane = new Person("jane"); 
     System.out.println("Adding jane to newPeople. Expect 0 changes."); 
     newPeople.add(jane); 

     System.out.println("Updating people to newPeople. Expect ? changes."); 
     people.set(newPeople); 

     Person jacob = new Person("jacob"); 
     System.out.println("Adding jacob to oldPeople. Expect 0 changes."); 
     oldPeople.add(jacob); 

     System.out.println("Adding jacob to newPeople. Expect 1 change."); 
     newPeople.add(jacob); 

     System.out.println("Changing jacob's name to jack. Expect 1 change."); 
     jacob.name.set("jack"); 

     System.out.println("Updating people to oldPeople. Expect ? changes."); 
     System.out.println(" oldPeople: " + oldPeople.toString()); 
     System.out.println(" newPeople : " + newPeople.toString()); 
     people.set(oldPeople); 
    } 

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

    class Person { 
     private final StringProperty name = new SimpleStringProperty(); 

     public Person(String name) { 
      this.name.set(name); 
     } 

     @Override 
     public String toString() { 
      return name.get(); 
     } 
    } 
} 

А вот выход из приведенного выше примера:

Adding john to oldPeople. Expect 1 change. 
    { [john] added at 0 } 
Changing john's name to joe. Expect 1 change. 
    { updated at range [0, 1) } 
Adding jane to newPeople. Expect 0 changes. 
Updating people to newPeople. Expect ? changes. 
    { [joe] removed at 0 } 
    { [jane] added at 0 } 
Adding jacob to oldPeople. Expect 0 changes. 
Adding jacob to newPeople. Expect 1 change. 
    { [jacob] added at 1 } 
Changing jacob's name to jack. Expect 1 change. 
    { updated at range [1, 2) } 
Updating people to oldPeople. Expect ? changes. 
oldPeople: [joe, jack] 
newPeople : [jane, jack] 
    { [jane, jack] removed at 0 } 
    { [joe, jack] added at 0 } 
Смежные вопросы