首页 > 解决方案 > JavaFX 任务/线程没有一致地填充表格视图

问题描述

所以,我需要使用 JavaFX 线程填充表格视图,但表格只填充了大约 70% 的时间。我正在查看我的代码,但我真的找不到问题出在哪里,我的猜测是在从 db 成功检索/处理数据之前,该任务正在以某种方式执行。谢谢是提前:)

private Executor exec;
private ObservableList<User> cellData = FXCollections.observableArrayList();
.
.
.
public void fillTable(HashMap<String,Object> whereClause){
        Task<List<User>> task = new Task<List<User>>(){
            @Override
            public ObservableList<User> call(){
                cellData.clear();
                cellData.addAll(userRepository.getAll(whereClause));
                userId.setCellValueFactory(new PropertyValueFactory<>("userID"));
                userName.setCellValueFactory(new PropertyValueFactory<>("userName"));
                userMail.setCellValueFactory(new PropertyValueFactory<>("userMail"));
                userPhone.setCellValueFactory(new PropertyValueFactory<>("userPhone"));
                isAdmin.setCellValueFactory(cellData -> {
                    String isAdminAsString = cellData.getValue().isAdmin() ? "Admin" : "Medic";
                    return new ReadOnlyStringWrapper(isAdminAsString);
                });
                isDeleted.setCellValueFactory(cellData -> {
                    String isActiveUser = cellData.getValue().isDeleted() ? "No" : "Yes";
                    return new ReadOnlyStringWrapper(isActiveUser);
                });
                logger.info("Cell values set");
                return cellData;
            }
        };
        exec.execute(task);
        task.setOnFailed(e -> System.out.println(task.getException().getMessage()));
        task.setOnSucceeded(e -> userTable.setItems((ObservableList<User>) task.getValue()));
        logger.info("Fill user Table Task executed");

标签: javajavafx

解决方案


您没有为正确、完全自信的答案提供足够的上下文,但我猜您遇到了与线程相关的问题。JavaFX 不是线程安全的;使用错误的线程更新 UI 可能会导致未定义的行为,例如数据仅出现约 70% 的时间。JavaFX 中有一条重要的规则,您必须始终遵循:

  • 切勿读取或写入直接或间接连接到除JavaFX 应用程序线程之外的线程上的实时场景图的对象的状态。

您的代码不遵循此规则。在您的call方法中,您正在对各种sTask进行结构修改cellData和设置。这导致所述对象被正在执行的任何线程修改。如果有任何提示,则该线程绝对不是JavaFX Application ThreadcellValueFactoryTableColumnTaskExecutor

我不确定你为什么首先在方法中设置cellValueFactoryTableColumn的 s call。单元格值工厂是只需要完成一次的配置——在您创建时TableColumn(或之后不久)。换句话说,在call方法中配置单元格值工厂是错误的,不仅因为它发生在后台线程上,还因为每次执行Task. 从方法中删除 set-the-cell-value-factory 代码,call并在需要时将其移动到创建TableColumns 的位置。如果您使用 FXML,并且TableColumn为您创建并注入了 s,那么控制器的initialize方法是进行此类配置的好地方。

您的cellData列表与TableView您的Task. 在后台线程上进行修改cellData将通知TableView同一线程上的这些更改(在进行更改的同一线程上调用侦听器)。简单的解决方案是让您的Task退货成为 List的,然后更新TableView如果成功。

Task<List<User>> task = new Task<List<User>>() {
    @Override protected List<User> call() throws Exception {
        return userRepository.getAll(whereClause);
    }
});
task.setOnSucceeded(event -> userTable.getItems().setAll(task.getValue()));
task.setOnFailed(event -> task.getException().printStackTrace());
exec.execute(task);

setAll方法ObservableList将首先清除列表,然后添加给定集合(或数组)的所有元素。这比调用clear其次更有效,addAll因为它只导致一个更改事件。此外,如果您想继续使用cellData,假设您之前已将其设置为表格的项目;只是使用cellData.setAll(task.getValue())


关于使用:

task.setOnSucceeded(e -> userTable.setItems((ObservableList<User>) task.getValue()));

由于您显然希望 aObservableList<User>被返回,因此您应该使用 aTask<ObservableList<User>>而不是 a Task<List<User>>。这将意味着getValue()返回ObservableList<User>,因此不需要演员表。但是,如果您遵循上述建议,那么这无关紧要。


推荐阅读