Я пытаюсь добавить/перезагрузить данные из ArrayList в TableView, нажав кнопку «Обновить» (в настоящее время я просто тестирую ее с одним столбцом TableView). Поскольку для TableView требуется ObservableList, я создал метод получения ObservableList, в котором ObservableList создается из существующего ArrayList (я протестировал оба списка, распечатав их значения без проблем). Каждый раз, когда я нажимаю кнопку перезагрузки, создается только количество строк, и я получаю сообщение об ошибке, вызванное этой строкой кода:
columnProject.setCellValueFactory(new PropertyValueFactory<Bill, String>("project"));
Я искал проблему в течение последних 3 часов и безуспешно пробовал все, что нашел в Stack Overflow.
Apr 26, 2019 10:23:16 PM javafx.scene.control.cell.PropertyValueFactory getCellDataReflectively
WARNING: Can not retrieve property 'project' in PropertyValueFactory: javafx.scene.control.cell.PropertyValueFactory@688d477d with provided class type: class net.strobl.main.Bill
java.lang.RuntimeException: java.lang.ClassCastException: class java.lang.String cannot be cast to class javafx.beans.property.ReadOnlyProperty (java.lang.String is in module java.base of loader 'bootstrap'; javafx.beans.property.ReadOnlyProperty is in module javafx.base of loader 'app')
at javafx.base/com.sun.javafx.property.PropertyReference.getProperty(PropertyReference.java:199)
at javafx.controls/javafx.scene.control.cell.PropertyValueFactory.getCellDataReflectively(PropertyValueFactory.java:182)
at javafx.controls/javafx.scene.control.cell.PropertyValueFactory.call(PropertyValueFactory.java:154)
at javafx.controls/javafx.scene.control.cell.PropertyValueFactory.call(PropertyValueFactory.java:133)
at javafx.controls/javafx.scene.control.TableColumn.getCellObservableValue(TableColumn.java:593)
at javafx.controls/javafx.scene.control.TableColumn.getCellObservableValue(TableColumn.java:578)
at javafx.controls/javafx.scene.control.TableCell.updateItem(TableCell.java:646)
at javafx.controls/javafx.scene.control.TableCell.indexChanged(TableCell.java:469)
at javafx.controls/javafx.scene.control.IndexedCell.updateIndex(IndexedCell.java:120)
at javafx.controls/javafx.scene.control.skin.TableRowSkinBase.updateCells(TableRowSkinBase.java:539)
at javafx.controls/javafx.scene.control.skin.TableRowSkinBase.<init>(TableRowSkinBase.java:159)
at javafx.controls/javafx.scene.control.skin.TableRowSkin.<init>(TableRowSkin.java:89)
at javafx.controls/javafx.scene.control.TableRow.createDefaultSkin(TableRow.java:213)
at javafx.controls/javafx.scene.control.Control.doProcessCSS(Control.java:897)
at javafx.controls/javafx.scene.control.Control.access$000(Control.java:83)
at javafx.controls/javafx.scene.control.Control$1.doProcessCSS(Control.java:89)
at javafx.controls/com.sun.javafx.scene.control.ControlHelper.processCSSImpl(ControlHelper.java:67)
at javafx.graphics/com.sun.javafx.scene.NodeHelper.processCSS(NodeHelper.java:145)
at javafx.graphics/javafx.scene.Node.processCSS(Node.java:9529)
at javafx.graphics/javafx.scene.Node.applyCss(Node.java:9616)
at javafx.controls/javafx.scene.control.skin.VirtualFlow.setCellIndex(VirtualFlow.java:1715)
at javafx.controls/javafx.scene.control.skin.VirtualFlow.getCell(VirtualFlow.java:1692)
at javafx.controls/javafx.scene.control.skin.VirtualFlow.getCellLength(VirtualFlow.java:1801)
at javafx.controls/javafx.scene.control.skin.VirtualFlow.computeViewportOffset(VirtualFlow.java:2639)
at javafx.controls/javafx.scene.control.skin.VirtualFlow.layoutChildren(VirtualFlow.java:1245)
at javafx.graphics/javafx.scene.Parent.layout(Parent.java:1204)
at javafx.graphics/javafx.scene.Parent.layout(Parent.java:1211)
at javafx.graphics/javafx.scene.Parent.layout(Parent.java:1211)
at javafx.graphics/javafx.scene.Parent.layout(Parent.java:1211)
at javafx.graphics/javafx.scene.Parent.layout(Parent.java:1211)
at javafx.graphics/javafx.scene.Parent.layout(Parent.java:1211)
at javafx.graphics/javafx.scene.Scene.doLayoutPass(Scene.java:576)
at javafx.graphics/javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2482)
at javafx.graphics/com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:412)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:411)
at javafx.graphics/com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:438)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:519)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:499)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:492)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:320)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(GtkApplication.java:277)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.ClassCastException: class java.lang.String cannot be cast to class javafx.beans.property.ReadOnlyProperty (java.lang.String is in module java.base of loader 'bootstrap'; javafx.beans.property.ReadOnlyProperty is in module javafx.base of loader 'app')
at javafx.base/com.sun.javafx.property.PropertyReference.getProperty(PropertyReference.java:197)
... 44 more
Примечание. Я удалил здесь некоторые методы, которые не имеют отношения к этой проблеме, для лучшей читабельности.
Билл Класс:
package net.strobl.main;
public class Bill {
private String project;
private boolean intake;
private Double amount;
private boolean digital;
private String date1;
private String date2;
private String date3;
private String shop;
private String reason;
private String person;
private boolean paid;
public Bill(String project, boolean intake, Double amount, boolean digital, String date1, String date2, String date3, String shop, String reason, String person, boolean paid) {
this.project = project;
this.intake = intake;
this.amount = amount;
this.digital = digital;
this.date1 = date1;
this.date2 = date2;
this.date3 = date3;
this.shop = shop;
this.reason = reason;
this.person = person;
this.paid = paid;
}
//region Getters and Setters
public String getProject() {
return project;
}
public String projectProperty() {
return project;
}
public void setProject(String project) {
this.project = project;
}
public boolean isIntake() {
return intake;
}
public void setIntake(boolean intake) {
this.intake = intake;
}
public Double getAmount() {
return amount;
}
public void setAmount(Double amount) {
this.amount = amount;
}
public boolean isDigital() {
return digital;
}
public void setDigital(boolean digital) {
this.digital = digital;
}
public String getDate1() {
return date1;
}
public void setDate1(String date1) {
this.date1 = date1;
}
public String getDate2() {
return date2;
}
public void setDate2(String date2) {
this.date2 = date2;
}
public String getDate3() {
return date3;
}
public void setDate3(String date3) {
this.date3 = date3;
}
public String getShop() {
return shop;
}
public void setShop(String shop) {
this.shop = shop;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public String getPerson() {
return person;
}
public void setPerson(String person) {
this.person = person;
}
public boolean isPaid() {
return paid;
}
public void setPaid(boolean paid) {
this.paid = paid;
}
//endregion
}
Класс контроллера:
package net.strobl.main;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import net.strobl.processing.DataManager;
import java.io.IOException;
public class Controller{
private App app;
private DataManager dataManager;
//region @FXML Objects new Bill
@FXML
Button submitButton;
@FXML
TextField inputProject;
@FXML
TextField inputAmount;
@FXML
TextField inputShop;
@FXML
TextField inputReason;
@FXML
TextField inputPerson;
@FXML
TextField inputDate1;
@FXML
TextField inputDate2;
@FXML
TextField inputDate3;
@FXML
RadioButton inputIntake;
@FXML
RadioButton inputDigital;
@FXML
RadioButton inputPaid;
//endregion
//region @FXML Objects viewProject
@FXML
Spinner<String> spinnerProjectSelect;
@FXML
TextField textFieldProjectSpent;
@FXML
TextField textFieldProjectGained;
@FXML
TextField textFieldProjectRevenue;
@FXML
ScrollPane scrollPane;
//region @FXML TableView and Columns
@FXML
TableView<Bill> tableView;
@FXML
TableColumn<Bill, String> columnProject;
@FXML
TableColumn<Bill, String> columnAmount;
@FXML
TableColumn<Bill, String> columnIntake;
@FXML
TableColumn<Bill, String> columnDigital;
@FXML
TableColumn<Bill, String> columnShop;
@FXML
TableColumn<Bill, String> columnReason;
@FXML
TableColumn<Bill, String> columnPerson;
@FXML
TableColumn<Bill, String> columnDate1;
@FXML
TableColumn<Bill, String> columnDate2;
@FXML
TableColumn<Bill, String> columnDate3;
@FXML
TableColumn<Bill, String> columnPaid;
public void addTableViewData(ActionEvent event){
columnProject.setCellValueFactory(new PropertyValueFactory<Bill, String>("project"));
tableView.setItems(getObservableBills());
}
private ObservableList<Bill> getObservableBills(){
return FXCollections.observableArrayList(app.getBills());
}
public Controller() throws IOException {
app = new App();
dataManager = new DataManager();
}
}
<?xml version = "1.0" encoding = "UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<TableView fx:id = "tableView" prefHeight = "585.0" prefWidth = "800.0">
<columns>
<TableColumn fx:id = "columnProject" prefWidth = "94.0" text = "Project" />
<TableColumn fx:id = "columnAmount" prefWidth = "66.0" text = "Amount" />
<TableColumn fx:id = "columnIntake" prefWidth = "57.0" text = "Intake" />
<TableColumn fx:id = "columnDigital" minWidth = "0.0" prefWidth = "54.0" text = "Digital" />
<TableColumn fx:id = "columnShop" minWidth = "1.0" prefWidth = "85.0" text = "Shop" />
<TableColumn fx:id = "columnReason" prefWidth = "86.0" text = "Reason" />
<TableColumn fx:id = "columnPerson" prefWidth = "87.0" text = "Person" />
<TableColumn fx:id = "columnDate1" prefWidth = "74.0" text = "Date 1" />
<TableColumn fx:id = "columnDate2" prefWidth = "64.0" text = "Date 2" />
<TableColumn fx:id = "columnDate3" prefWidth = "64.0" text = "Date 3" />
<TableColumn fx:id = "columnPaid" prefWidth = "68.0" text = "Paid" />
</columns>
</TableView>
<Button fx:id = "buttonReload" mnemonicParsing = "false" onAction = "#addTableViewData" prefHeight = "28.0" prefWidth = "140.0" text = "Reload" GridPane.rowIndex = "2" />
module AutomatedFinances {
requires javafx.controls;
requires javafx.fxml;
requires opencsv;
opens net.strobl.main;
}
@JoséPeda Я попробовал второй комментарий отсюда stackoverflow.com/questions/47181969/… Я, вероятно, неправильно его понял
@kleopatra, спасибо, я как-то об этом не знал (теперь знаю, что должен был знать). В следующий раз учту!




Я не специалист по JavaFX. Но если я правильно прочитал документы, параметр String в конструкторе PropertyValueFactory относится не к полю параметра типа TableView, а к параметру String конструктора TableColumn. Если я прав, это должно быть не "project" (приватное поле в Билле), а "Project" (текст параметра первого столбца таблицы в FXML).
спасибо за ответ, но это не решение моего вопроса. Я, наверное, плохо объяснил (не носитель языка).
В вашем Bill классе у вас есть:
public class Bill {
private String project;
// getter
public String getProject() {
return project;
}
// setter
public void setProject(String project) {
this.project = project;
}
// ????
public String projectProperty() {
return project;
}
}
Когда вы используете обратный вызов new PropertyValueFactory("project") для фабрики значений ячеек TableColumn, вот что происходит, когда оценивается метод call:
@Override
public ObservableValue<T> call(CellDataFeatures<S,T> param) {
return getCellDataReflectively(param.getValue());
}
private ObservableValue<T> getCellDataReflectively(S rowData) {
...
if (propertyRef.hasProperty()) {
return propertyRef.getProperty(rowData);
} else {
T value = propertyRef.get(rowData);
return new ReadOnlyObjectWrapper<T>(value);
}
...
}
В первую очередь есть попытка использовать свойство JavaFX, где в PropertyReference вы можете увидеть:
public boolean hasProperty() {
reflect();
return propertyGetter != null;
}
Поскольку вы предоставляете метод projectProperty(), этот метод ошибочно воспринимается как propertyGetter, поскольку он заканчивается на Property в reflect():
// Now attempt to look for the property-getter.
final String propertyGetterName = name + "Property";
поэтому он вернет true, а затем getProperty() попытается применить это свойство:
try {
return (ReadOnlyProperty<T>)MethodHelper.invoke(propertyGetter, bean, (Object[])null);
}
и это вызовет исключение, которое вы получите:
Caused by: java.lang.ClassCastException: class java.lang.String cannot be cast to class javafx.beans.property.ReadOnlyProperty (java.lang.String is in module java.base of loader 'bootstrap'; javafx.beans.property.ReadOnlyProperty is in module javafx.base of loader 'app')
at javafx.base/com.sun.javafx.property.PropertyReference.getProperty(PropertyReference.java:197)
так как ваш String project не может быть явно переведен на ReadOnlyProperty.
Решение
Вы либо используете правильное свойство:
private final StringProperty project = new SimpleStringProperty();
public final StringProperty projectProperty() {
return project;
}
public final String getProject() {
return project.get();
}
public final void setProject(String value) {
project.set(value);
}
Или вы удалите неправильный метод получения свойства, и тогда обратный вызов по умолчанию будет использовать второй случай, используя примитив String в getCellDataReflectively, который будет преобразован в свойство только для чтения:
T value = propertyRef.get(rowData);
return new ReadOnlyObjectWrapper<T>(value);
Хотя это работает нормально, предпочтительнее первый вариант с использованием правильных свойств JavaFX.
Спасибо, теперь все работает отлично! Я даже не ожидал такого подробного ответа не просто с решением, а с полным объяснением за такое небольшое время.
У меня была несколько похожая проблема с сообщением об ошибке:
Dec 28, 2021 10:14:33 PM javafx.scene.control.cell.PropertyValueFactory getCellDataReflectively
WARNING: Can not retrieve property 'risk' in PropertyValueFactory: javafx.scene.control.cell.PropertyValueFactory@14aa73de with provided class type: class derivative.generator.derivative.LiabilityEX
java.lang.IllegalStateException: Cannot read from unreadable property risk
чтобы решить эту проблему, я добавил геттеры ко всем полям класса объекта, который я пытался добавить в столбец.
Пример для моего случая
public int getRisk() {
return risk;
}
Сначала я столкнулся с той же проблемой, но для ее решения я просто добавил геттеры и сеттеры (хотя и не используемые в коде явно) класса «Билл» (или любого класса, чей TableView вы хотите создать).
Почему у вас есть этот метод
Bill:projectProperty? Он не возвращает свойство...