延迟加载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
实例的引用,因此它们是垃圾回收的。
有时它们中的一些在任务完成之前被垃圾收集。
在这种情况下,不会调用服务的onFailed
和onSucceeded
属性的侦听器。
简单的解决方案是使用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 () { ...