JavaFX任务/线程无法连续填充tableview

时间:2019-04-26 23:27:20

标签: java 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");

1 个答案:

答案 0 :(得分:3)

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

  • 除了在 JavaFX Application Thread 之外的其他线程上,请勿读写直接或间接连接到实时场景图的对象的状态。

您的代码不遵循此规则。在call的{​​{1}}方法内部,您正在结构上修改Task并设置各种cellData的{​​{1}}。这导致所述对象被执行cellValueFactory的任何线程修改。如果TableColumn是任何提示,则该线程绝对不是 JavaFX Application线程

我不确定为什么首先要在Task方法中设置Executor的{​​{1}}。单元格值工厂是仅需执行一次的配置-创建cellValueFactory时(或之后不久)。换句话说,在TableColumn方法中配置单元格值工厂是错误的,这不仅是因为它发生在后台线程上,而且还因为每次执行call时都会发生。从TableColumn方法中删除“设置单元格值工厂”代码,如果需要,将其移动到创建call的位置。如果您使用的是FXML,并且为您创建了Task并进行了注入,那么控制器的call方法就是进行这种配置的好地方。

您的TableColumn列表已连接到您的TableColumn,如果不是第一次,则肯定是在第一次成功执行initialize之后。在后台线程上修改cellData将通知TableView在同一线程上的那些更改(在进行更改的同一线程上调用侦听器)。一种简单的解决方案是让您的Task返回一个 new cellData,如果成功,则更新TableView

Task

List的{​​{1}}方法将首先清除列表,然后添加给定集合(或数组)的所有元素。这比调用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())); 返回ObservableList<User>,因此不需要强制类型转换。但是,如果您按照上述建议进行操作,则无关紧要。