Javafx 在TableView中延迟加载项
我正在实现一个自定义TableCellFactory,以便从数据库异步加载数据。查询数据库不应阻塞UI线程Javafx 在TableView中延迟加载项,javafx,tableview,Javafx,Tableview,我正在实现一个自定义TableCellFactory,以便从数据库异步加载数据。查询数据库不应阻塞UI线程 public abstract class AsynchronousTableCellFactory<E, T> implements Callback<TableColumn<E, T>, TableCell<E, T>> { private static final Executor exe1 = Executors.newFixedT
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);
}
}
编辑:找到更好的方法。请注意
表格单元格是可重用的!这意味着可以将任何项分配给任何单元格(这就是updateItem
方法的作用)。当您的完成处理程序直接设置TableCell
的文本时,您当前的代码可能会带来很多问题
一个简单的解决方案是将结果包装在LazyLoadingOriginWrapper
内的observeValue
中,并将其用于CellValueFactory
中,而不是创建自己的TableCell
。observeValue
的更新将在TableView
中可见(请确保在“fx应用程序”线程上修改observeValue
)。e、 g
请注意,TableCell
是可重用的!这意味着可以将任何项分配给任何单元格(这就是updateItem
方法的作用)。当您的完成处理程序直接设置TableCell
的文本时,您当前的代码可能会带来很多问题
一个简单的解决方案是将结果包装在LazyLoadingOriginWrapper
内的observeValue
中,并将其用于CellValueFactory
中,而不是创建自己的TableCell
。observeValue
的更新将在TableView
中可见(请确保在“fx应用程序”线程上修改observeValue
)。e、 g
谢谢,就这样,我想起来了!使用CellValueFactory而不是CellFactory。更新LazyLoadingElementWrapper中的ObservableValue是最干净的方法,非常感谢!回答得好-只是有点吹毛求疵:确保更新fx应用程序Thread上的wrapper属性谢谢,就是这样,刚刚搞定!使用CellValueFactory而不是CellFactory。更新LazyLoadingElementWrapper中的ObservableValue是最干净的方法,非常感谢!回答得好-只是有点吹毛求疵:确保更新fx应用程序线程的包装器属性只是为了强调:永远不要在单元格中执行任何繁重的操作!这是数据层的任务,始终(正如n247s在中正确完成的那样)只是为了强调:永远不要在单元中执行任何繁重的操作!这始终是数据层的任务(如中n247s所正确完成的)
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()));
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());
}