【发布时间】:2018-08-08 18:43:47
【问题描述】:
这是here的后续报道。
我正在实现一个表格,它将数据异步加载到表格单元格中。问题是,表格单元格有时不能正确更新。有时它会以某种方式“挂起”并永远显示“Loading..”。只有当我在表格中滚动一点时,实际值才会更新。
要重现,运行应用程序并在表格中快速向下滚动。有些单元格不会显示“延迟加载”值,而是显示占位符字符串。
延迟加载属性如下所示:
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<String> s = new Service<String>() {
@Override
protected Task<String> 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<String> createTask();
}
应用程序如下所示:
public class ExampleTable extends Application {
private static final int NUM_ELEMENTS = 500;
private final TableView<ExampleBean> table = new TableView<>();
private final ObservableList<ExampleBean> 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<ExampleBean, String> c1 = new TableColumn<>("A");
c1.setCellValueFactory(new PropertyValueFactory<ExampleBean, String>("p1"));
final TableColumn<ExampleBean, String> c2 = new TableColumn<>("B");
c2.setCellValueFactory(new PropertyValueFactory<ExampleBean, String>("p2"));
final TableColumn<ExampleBean, String> c3 = new TableColumn<>("C");
c3.setCellValueFactory(new PropertyValueFactory<ExampleBean, String>("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();
}
}
完整的可运行代码可以在here找到。
【问题讨论】:
-
我认为缺少的是您需要在
onSucceeded()和onFailed()方法中调用fireValueChangedEvent(),以便在有新值可用时通知听众。 -
这是/应该在
setValue()方法中完成,不是吗?我会试一试的。 -
但是当加载任务完成时
setValue()不会被调用... -
哦,但是你调用它之前你调用
setLoaded()...所以听众会收到通知,但他们仍然得到“尚未加载”的价值 -
嗯。这项服务似乎完全没有必要;您真正需要的只是一项任务(只使用一次服务是没有意义的:它的全部目的是它是可重用的)。我添加了
fireValueChangeEvent()调用并删除了该服务(仅使用一个任务),然后它运行良好。不过这让我很困扰:我不知道为什么该服务会阻止它正常工作。