Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javafx Task<ObserveableList>.updateValue only fires first change event

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".

Minimum Example

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;
    }
}
like image 233
Autumn Avatar asked Dec 30 '25 01:12

Autumn


1 Answers

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:

  1. Update the list on the FX Application thread, by calling Platform.runLater(() -> list.add(addMe));
  2. Arrange to observe the list itself, instead of the task's value property.
like image 180
James_D Avatar answered Dec 31 '25 18:12

James_D