Вы не даете достаточно контекста для правильного, полностью уверенного ответа, но я предполагаю, что вы сталкиваетесь с проблемами, связанными с потоками.JavaFX не является потокобезопасным;использование неправильного потока для обновления пользовательского интерфейса может привести к неопределенному поведению , так как данные появляются только в ~ 70% случаев.В JavaFX есть важное правило, которому вы должны всегда следовать:
- Никогда не читайте и не записывайте состояние объектов, которые прямо или косвенно связаны с графом живой сцены напоток, отличный от Поток приложения JavaFX .
Ваш код не соответствует этому правилу.Внутри call
метода вашего Task
вы структурно модифицируете cellData
и устанавливаете cellValueFactory
различных TableColumn
с.Это приводит к тому, что указанные объекты модифицируются тем потоком, который выполняет Task
.Если Executor
является какой-либо подсказкой, этот поток определенно не является Потоком приложения JavaFX .
Я не уверен, почему вы устанавливаете cellValueFactory
вашего TableColumn
s внутри метода call
в первую очередь.Фабрика значений ячеек - это конфигурация, которую необходимо выполнить только один раз - при создании TableColumn
(или вскоре после этого).Другими словами, настройка фабрики значений ячеек в методе call
неверна не только потому, что это происходит в фоновом потоке, но и потому, что это происходит каждый раз, когда вы выполняете Task
.Удалите код set-the-cell-value-factory из метода call
и переместите его, если необходимо, туда, где вы создаете TableColumn
s.Если вы используете 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()));
Поскольку вы явно ожидаете, что ObservableList<User>
будет возвращено, вы должны использовать Task<ObservableList<User>>
вместо Task<List<User>>
.Это будет означать, что getValue()
возвращает ObservableList<User>
, и, следовательно, приведение становится ненужным.Однако, если вы последуете совету выше, это не имеет значения.