Javafx Task <observeablelist> .updateValue仅触发第一个更改事件

时间:2015-11-08 17:28:46

标签: multithreading javafx javafx-8

我想观察任务的valueProperty,并在updateValue()更改时执行操作。改变事件似乎只是在第一次更新时被触发了。

Oracle's getValue document有一个部分暗示重复调用UpdateValue以返回部分结果是犹太的。也许我不理解“更新合并”的含义。

最低范例

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;
    }
}

2 个答案:

答案 0 :(得分:4)

任务的value只会更改一次。最初是null。在第一次迭代时,它会更改value==list以及每次后续迭代value==list(即没有进一步的更改)。

你可能想:

  1. 通过调用Platform.runLater(() -> list.add(addMe));
  2. 更新FX应用程序线程上的列表
  3. 安排观察列表本身,而不是任务的value属性。

答案 1 :(得分:3)

对于后来出现的人来说,这里的代码经过修改后可以直接访问listView本身。

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));
                }
            }
        });
    }


}