Reputation: 3746
I'd like to observe a task's valueProperty, and take action when it's changed by updateValue(). The change event only seems to get fired on the first update though.
Oracle's getValue document has a section implying that it's kosher to repeatedly call UpdateValue to return partial results. Perhaps I'm not understanding what they mean by "updates are coalesced".
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.stage.Stage;
public class Main extends Application {
MyTask task = new MyTask();
ListView<String> listView = new ListView<>();
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(listView));
primaryStage.show();
Thread taskThread = new Thread(task);
taskThread.start();
task.valueProperty().addListener( (ob,old,nw) -> listView.getItems().addAll(nw) ); // Only fires once.
//task.lastString.addListener( iv -> listView.getItems().addAll(task.lastString.getValue()) ); // Fires every add
}
public static void main(String[] args) { launch(args); }
}
class MyTask extends Task<ObservableList<String>> {
ObservableList<String> list = FXCollections.observableArrayList();
public ReadOnlyObjectWrapper<String> lastString = new ReadOnlyObjectWrapper<>(new String());
Integer maxWork = 4;
@Override
protected ObservableList<String> call() throws Exception {
for( Integer stringNo=0; stringNo<maxWork; stringNo++) {
Thread.sleep(500);
String addMe = new String("Thread string " + stringNo);
list.add(addMe);
updateProgress(stringNo, maxWork);
updateValue(list); // Only fires one change event
Platform.runLater( () -> {
lastString.setValue(addMe); // Works as expected.
} );
}
return list;
}
}
Upvotes: 3
Views: 1784
Reputation: 3746
For anyone coming along later, here's the code, modified to provide direct access to the listView itself.
package demo;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.stage.Stage;
import java.util.List;
public class Main extends Application {
MyTask task = new MyTask();
ListView<String> listView = new ListView<>();
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(listView));
primaryStage.show();
task.lv = listView;
Thread taskThread = new Thread(task);
taskThread.start();
}
public static void main(String[] args) {
launch(args);
}
}
class MyTask extends Task<ObservableList<String>> {
ObservableList<String> list = FXCollections.observableArrayList();
public ReadOnlyObjectWrapper<String> lastString = new ReadOnlyObjectWrapper<>(new String());
Integer maxWork = 4;
ListView lv;
@Override
protected ObservableList<String> call() throws Exception {
updateValue(list); // make sure that the task's valueproperty has a value.
Platform.runLater(() -> addMyListener());
for (Integer stringNo = 0; stringNo < maxWork; stringNo++) {
Thread.sleep(500);
String addMe = new String("Thread string " + stringNo);
list.add(addMe);
updateProgress(stringNo, maxWork);
updateValue(list); // Only fires one change event
Platform.runLater(() -> {
lastString.setValue(addMe); // Works as expected.
});
}
return list;
}
void addMyListener() {
ObservableList<String> obl = valueProperty().getValue();
obl.addListener(new ListChangeListener<String>() {
@Override
public void onChanged(Change<? extends String> c) {
while (c.next()) {
List added = c.getAddedSubList();
Platform.runLater(() -> lv.getItems().addAll(added));
}
}
});
}
}
Upvotes: 3
Reputation: 209340
The value
of the task only changes once. Initially it is null
. On the first iteration it changes so the value==list
and on every subsequent iteration, value==list
(i.e. there are no further changes).
You probably want to:
Platform.runLater(() -> list.add(addMe));
value
property.Upvotes: 4