延迟加载FXProperties

这是此后的后续行动。

我正在实现一个表,它将数据异步加载到表格单元格中。 问题是,表格单元有时不能正确更新。 有时它会以某种方式“挂起”并永远显示“正在加载……” 。 只有当我在表格中滚动一点时,实际值才会更新。

在此处输入图像描述

要重现 ,请在表格中快速向下滚动应用程序。 某些单元格不会显示“延迟加载”值,而是占位符字符串。

延迟加载属性如下所示:

public abstract class LazyLoadingStringProperty extends SimpleStringProperty { public static final String DEFAULT_LOADING_STRING = "Loading.."; private static final ExecutorService exe = Executors.newCachedThreadPool(); private String loadingString = DEFAULT_LOADING_STRING; private boolean loaded = false; public LazyLoadingStringProperty() { } public boolean isLoaded() { return loaded; } public void setLoaded(final boolean loaded) { this.loaded = loaded; } public String getLoadingString() { return loadingString; } public void setLoadingString(final String loadingString) { this.loadingString = loadingString; } @Override public String getValue() { if (!loaded) { Platform.runLater(() -> startLoadingService()); return loadingString; } return super.getValue(); } protected void startLoadingService() { final Service s = new Service() { @Override protected Task createTask() { return LazyLoadingStringProperty.this.createTask(); } }; s.setExecutor(exe); s.setOnFailed(e -> { setLoaded(true); setValue(s.getException().getLocalizedMessage()); }); s.setOnSucceeded(e -> { setLoaded(true); setValue(s.getValue()); }); s.start(); // System.err.println("Started"); } protected abstract Task createTask(); 

}

Applikation看起来像这样:

 public class ExampleTable extends Application { private static final int NUM_ELEMENTS = 500; private final TableView table = new TableView(); private final ObservableList data = FXCollections.observableArrayList(); public static void main(final String[] args) { launch(args); } @Override public void start(final Stage stage) { final Scene scene = new Scene(new Group()); stage.setTitle("Table View Sample"); stage.setWidth(300); stage.setHeight(500); final TableColumn c1 = new TableColumn("A"); c1.setCellValueFactory(new PropertyValueFactory("p1")); final TableColumn c2 = new TableColumn("B"); c2.setCellValueFactory(new PropertyValueFactory("p2")); final TableColumn c3 = new TableColumn("C"); c3.setCellValueFactory(new PropertyValueFactory("p3")); c1.setPrefWidth(100); c2.setPrefWidth(100); c3.setPrefWidth(100); for (int i = 0; i < NUM_ELEMENTS; i++) { data.add(new ExampleBean()); } final ScrollPane sp = new ScrollPane(); sp.setContent(table); sp.setMaxHeight(Double.POSITIVE_INFINITY); sp.setMaxWidth(Double.POSITIVE_INFINITY); sp.setFitToHeight(true); sp.setFitToWidth(true); table.setItems(data); // table.setMaxHeight(Double.POSITIVE_INFINITY); // table.setMaxWidth(Double.POSITIVE_INFINITY); table.getColumns().addAll(c1, c2, c3); final VBox vbox = new VBox(); vbox.setSpacing(5); VBox.setVgrow(sp, Priority.ALWAYS); vbox.getChildren().addAll(sp); scene.setRoot(vbox); stage.setScene(scene); stage.show(); } } 

完整的可运行代码可以在这里找到。

它看起来像JavaFX服务中的一个错误……

由于没有保存对Service实例的引用,因此它们是垃圾回收的。
有时它们中的一些在任务完成之前被垃圾收集。
在这种情况下,不会调用服务的onFailedonSucceeded属性的侦听器。

简单的解决方案是使用LazyLoadingStringProperty对象保存服务引用:

 ... private ArrayList> services = new ArrayList<>(); protected void startLoadingService() { final Service s = new Service() { @Override protected Task createTask() { return LazyLoadingStringProperty.this.createTask(); } }; services.add(s); ... 

但是没有必要并行加载一个具有许多任务的值。 因此,每个值的一项服务绰绰有余:

  ... private Service s; protected void startLoadingService() { if (s != null) return; // started already s = new Service() { ...