TableView中的延迟加载项 [英] Lazy-loading items in TableView
问题描述
我正在实现一个自定义TableCellFactory来从数据库异步加载数据。查询数据库不应该阻止UI线程。
I am implementing a custom TableCellFactory to load data asynchronously from a database. Querying the db should not block the UI thread.
public abstract class AsynchronousTableCellFactory<E, T> implements Callback<TableColumn<E, T>, TableCell<E, T>> {
private static final Executor exe1 = Executors.newFixedThreadPool(2);
private static final Executor exe2 = Executors.newFixedThreadPool(2);
@Override
public TableCell<E, T> call(final TableColumn<E, T> param) {
final TableCell<E, T> cell = new TableCell<E, T>() {
@Override
public void updateItem(final T item, final boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
setText("Thinking..");
// do this later some time, we need to finish here FAST
exe1.execute(() -> {
if (getTableRow() != null) {
final Service<T> service = new Service<T>() {
@Override
protected Task<T> createTask() {
return getTask((E) getTableRow().getItem());
}
};
service.setExecutor(exe2);
service.setOnSucceeded(e -> {
if (e.getSource().getValue() == null) {
setText("n/a");
} else {
setText(e.getSource().getValue().toString());
}
});
service.setOnFailed(e -> {
final Throwable t = e.getSource().getException();
setText(t.getLocalizedMessage());
});
service.start();
}
});
}
}
};
return cell;
}
protected abstract Task<T> getTask(E rowDataItem);
}
它大部分时间都在工作,但并非总是如此。一个奇怪的事情是 T item
总是 null
。
在哪种情况下我可以指望 T item
null
?
It is working most of the time, but not always. One strange thing about it is that T item
is always null
.
In which cases can I expect T item
to be null
?
相应的表格代码:
public class OriginsTableViewController implements Initializable {
@FXML
private TableView<LazyLoadingOriginWrapper> table;
@FXML
private TableColumn<LazyLoadingOriginWrapper, String> cSampleName;
@Override
public void initialize(final URL location, final ResourceBundle resources) {
cSampleName.setCellFactory(new AsynchronousOriginTableCellFactory<>(e -> e.getSampleName()));
}
编辑: 找到更好的方法此处。
推荐答案
请注意 TableCell
是可重用的!这意味着任何项目都可以分配给任何单元格(这是 updateItem
方法的用途)。当您的完成处理程序直接设置 TableCell
的文本时,您当前的代码可能会出现很多问题。
Be aware that a TableCell
is reusable! this means any item can be assigned to any cell (that's what the updateItem
method is for). Your current code can provide a lot of problems as your completion handlers set a TableCell
's text directly.
简单的解决方案是将结果包装在 LazyLoadingOriginWrapper
中的 ObservableValue
中,并在 CellValueFactory中使用它
而不是创建自己的 TableCell
。 ObservableValue
的更新将在 TableView
中保持不变(请确保修改 ObservableValue < /'c $ c>在'fx-application'线程上!)。例如
An easy solution is wrapping the result in a ObservableValue
inside your LazyLoadingOriginWrapper
and use that in a CellValueFactory
rather than creating your own TableCell
. Updates of the ObservableValue
will be vissible inside the TableView
(make sure you modify the ObservableValue
on the 'fx-application' thread!). e.g.
public static class LazyLoadingOriginWrapper
{
// params are 'bean', 'id/name', 'default value'
ObservableStringValue name = new SimpleStringProperty(this, "name", "thinking...");
public ObservableStringValue nameProperty() { return name; }
public void setName(String value)
{
Platform.runLater(() -> name.set(value));
}
// add your logic for asynchronious loading here and update the above ObservableValue instead. Make sure you trigger it manually!
}
@Override
public void initialize(final URL location, final ResourceBundle resources) {
cSampleName.setCellValueFactory(C -> C.getValue().nameProperty());
}
这篇关于TableView中的延迟加载项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!