JavaFX PropertyValueFactory не может получить свойство - PullRequest
0 голосов
/ 27 апреля 2019

Резюме:

Я пытаюсь добавить / перезагрузить данные из ArrayList в TableView, нажав кнопку «Перезагрузить» (в настоящее время я просто тестирую ее с одним столбцом TableView). Поскольку для TableView требуется ObservableList, я создал метод получения ObservableList, в котором ObservableList создается из существующего ArrayList (я протестировал оба списка, печатая их значения без проблем). Каждый раз, когда я нажимаю кнопку перезагрузки, создается только количество строк, и я получаю сообщение об ошибке, вызванное этой строкой кода:

columnProject.setCellValueFactory(new PropertyValueFactory<Bill, String>("project"));

Я искал проблему последние 3 часа и безуспешно пробовал все, что нашел в переполнении стека.

Информация:

  • Java: openjdk 11.0.3 и Javafx-11
  • Операционная система: Manjaro Linux с ядром 5.0.7-1
  • IDE: IntelliJ Idea Ultimate 2019
  • Я использую FXML с SceneBuilder для всех объектов JavaFX

Ошибка:

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();

    }

}

fxml-файл (только кнопка и TableView):

<?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-info.java

module AutomatedFinances {
    requires javafx.controls;
    requires javafx.fxml;
    requires opencsv;

    opens net.strobl.main;
}

Ответы [ 2 ]

2 голосов
/ 27 апреля 2019

В вашем 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.

0 голосов
/ 27 апреля 2019

Я не специалист по JavaFX. Но если я правильно прочитал документы, параметр String в конструкторе PropertyValueFactory ссылается не на поле параметра типа TableView, а на параметр String конструктора TableColumn. Если я прав, это должно быть не "project" (личное поле в Bill), а "Project" (текст параметра первого TableColumn в FXML).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...