连接TableColumn对象到自定义类的问题

huangapple go评论19阅读模式
英文:

Problem connecting TableColumn objects to custom class

问题

I've been trying to figure out why this scene keeps throwing an error when I try to add rows to it's TableView. I've been looking through the tutorial at https://docs.oracle.com/javase/8/javafx/user-interface-tutorial/table-view.htm#sthref109, but I haven't been able to figure out what I'm doing wrong. I am sure I am able to query the data and put it into my ObservableList object, because I can print the values out afterwards.

It seems like there's something wrong with how I'm connecting the TableColumns to the custom CropCrow class I'm using.

How can I get the TableColumns to accept my custom class values?

The types of warnings I get:

WARNING: Can not retrieve property 'speciesName' in PropertyValueFactory: javafx.scene.control.cell.PropertyValueFactory@5e065707 with provided class type: class main.gui.CropsController$CropRow

Commenting out this line removes the warning, but I can't add rows without it.

this.cropTable.getItems().addAll(data);

My Code:

package main.gui;

import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import main.GardenersApp;
import main.database.Database;

import java.sql.ResultSet;
import java.sql.SQLException;

public final class CropsController extends SuperController {

    private static final ObservableList<CropRow> data = FXCollections.observableArrayList();

    @FXML
    private TableView<CropRow> cropTable;

    @FXML
    private TableColumn<CropRow, String> speciesCol;

    @FXML
    private TableColumn<CropRow, String> varietyCol;

    @FXML
    private TableColumn<CropRow, String> typeCol;

    @FXML
    private TableColumn<CropRow, String> soilTempCol;

    @FXML
    private TableColumn<CropRow, String> weeklyWaterCol;

    @FXML
    private TableColumn<CropRow, String> sunlightCol;

    @FXML
    private TableColumn<CropRow, String> pHCol;

    @FXML
    private Button addCrop;

    @FXML
    private Button deleteCrop;

    @FXML
    private Button returnToMain;

    /**
     * Called immediately when this scene is shown.
     * Retrieves relevant data from the database and displays it in the
     */
    @FXML
    private void initialize() throws SQLException {
        Database.connect();

        ResultSet cropTable = Database.runQuery(
                "SELECT SpeciesName, VarietyName, CropTypeName, SoilTemperatureMin, SoilTemperatureMax, SunlightNeededMin, SunlightNeededMax, WaterNeededMin, WaterNeededMax, PHMin, PHMax \n" +
                        "    FROM CropSpecies CROSS JOIN CropVariety CROSS JOIN CropType \n" +
                        "    ON CropSpecies.SpeciesNumber = CropVariety.SpeciesNumber \n" +
                        "        AND CropType.CropTypeNumber = CropVariety.CropTypeNumber \n" +
                        "    GROUP BY SpeciesName, VarietyName, CropTypeName, SoilTemperatureMin, SoilTemperatureMax, SunlightNeededMin, SunlightNeededMax, WaterNeededMin, WaterNeededMax, PHMin, PHMax");

        while (cropTable.next()) {

            String speciesName = cropTable.getString("SpeciesName");
            String varietyName = cropTable.getString("VarietyName");
            String cropTypeName = cropTable.getString("CropTypeName");
            String soilTemperatureMin = cropTable.getString("SoilTemperatureMin");
            String soilTemperatureMax = cropTable.getString("SoilTemperatureMax");
            String sunlightNeededMin = cropTable.getString("SunlightNeededMin");
            String sunlightNeededMax = cropTable.getString("SunlightNeededMax");
            String waterNeededMin = cropTable.getString("WaterNeededMin");
            String waterNeededMax = cropTable.getString("WaterNeededMax");
            String pHMin = cropTable.getString("PHMin");
            String pHMax = cropTable.getString("PHMax");

            String soilTemperatureRange = soilTemperatureMin+ " - " + soilTemperatureMax + " F";
            String sunlightNeededRange = sunlightNeededMin + " - " + sunlightNeededMax + " Hours";
            String waterNeededRange = waterNeededMin + " - " + waterNeededMax + " inches";
            String pHRange = pHMin + " - " + pHMax;

            CropRow cropRow = new CropRow(speciesName, varietyName, cropTypeName, soilTemperatureRange, sunlightNeededRange, waterNeededRange, pHRange);
            data.add(cropRow);
        }

        this.speciesCol.setCellValueFactory(
                new PropertyValueFactory<CropRow, String>("speciesName"));

        this.varietyCol.setCellValueFactory(
                new PropertyValueFactory<CropRow, String>("varietyName"));

        this.typeCol.setCellValueFactory(
                new PropertyValueFactory<CropRow, String>("cropTypeName"));

        this.soilTempCol.setCellValueFactory(
                new PropertyValueFactory<CropRow, String>("soilTemperature"));

        this.weeklyWaterCol.setCellValueFactory(
                new PropertyValueFactory<CropRow, String>("waterNeeded"));

        this.sunlightCol.setCellValueFactory(
                new PropertyValueFactory<CropRow, String>("sunlightNeeded"));

        this.pHCol.setCellValueFactory(
                new PropertyValueFactory<CropRow, String>("pH"));

        for(CropRow cr : data) {
            System.out.println(cr.printCrop());
        }

        this.cropTable.setItems(data);

        Database.disconnect();
    }

    @FXML
    private void addCropAction() { }

    @FXML
    private void deleteCropAction() { }

    /**
     * Returns to the title screen when the returnToMain Button is clicked.
     */
    @FXML
    private void returnToMainAction() {
        GardenersApp gApp = GetG_app();

        gApp.switchScreen(Page.TITLE);
    }

    /**
     * Holds data on a row in the cropTable TableView object.
     */
    private static class CropRow {

        private final SimpleStringProperty speciesName;
        private final SimpleStringProperty varietyName;
        private final SimpleStringProperty cropTypeName;
        private final SimpleStringProperty soilTemperature;
        private final SimpleStringProperty waterNeeded;
        private final SimpleStringProperty sunlightNeeded;
        private final SimpleStringProperty pH;

        private CropRow(String species, String variety, String cropType, String soilTemp, String water, String sunlight, String pH) {

            this.speciesName = new SimpleStringProperty(species);
            this.varietyName = new SimpleStringProperty(variety);
            this.cropTypeName = new SimpleStringProperty(cropType);
            this.soilTemperature = new SimpleStringProperty(soilTemp);
            this.waterNeeded = new SimpleStringProperty(water);
            this.sunlightNeeded = new SimpleStringProperty(sunlight);
            this.pH = new SimpleStringProperty(pH);
        }

        public String getSpeciesName() {
            return speciesName.get();
        }

        public void setSpeciesName(String speciesName) {
            this.speciesName.set(speciesName);
英文:

I've been trying to figure out why this scene keeps throwing an error when I try to add rows to it's TableView. I've been looking through the tutorial at https://docs.oracle.com/javase/8/javafx/user-interface-tutorial/table-view.htm#sthref109, but I haven't been able to figure out what I'm doing wrong. I am sure I am able to query the data and put it into my ObservableList object, because I can print the values out afterwards.

It seems like there's something wrong with how I'm connecting the TableColumns to the custom CropCrow class I'm using.

How can I get the TableColumns to accept my custom class values?

The types of warnings I get:

WARNING: Can not retrieve property &#39;speciesName&#39; in PropertyValueFactory: javafx.scene.control.cell.PropertyValueFactory@5e065707 with provided class type: class main.gui.CropsController$CropRow

Commenting out this line removes the warning, but I can't add rows without it.

this.cropTable.getItems().addAll(data);

My Code:

package main.gui;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import main.GardenersApp;
import main.database.Database;
import java.sql.ResultSet;
import java.sql.SQLException;
public final class CropsController extends SuperController {
private static final ObservableList&lt;CropRow&gt; data = FXCollections.observableArrayList();
@FXML
private TableView&lt;CropRow&gt; cropTable;
@FXML
private TableColumn&lt;CropRow, String&gt; speciesCol;
@FXML
private TableColumn&lt;CropRow, String&gt; varietyCol;
@FXML
private TableColumn&lt;CropRow, String&gt; typeCol;
@FXML
private TableColumn&lt;CropRow, String&gt; soilTempCol;
@FXML
private TableColumn&lt;CropRow, String&gt; weeklyWaterCol;
@FXML
private TableColumn&lt;CropRow, String&gt; sunlightCol;
@FXML
private TableColumn&lt;CropRow, String&gt; pHCol;
@FXML
private Button addCrop;
@FXML
private Button deleteCrop;
@FXML
private Button returnToMain;
/**
* Called immediately when this scene is shown.
* Retrieves relevant data from the database and displays it in the
*/
@FXML
private void initialize() throws SQLException {
Database.connect();
ResultSet cropTable = Database.runQuery(
&quot;SELECT SpeciesName, VarietyName, CropTypeName, SoilTemperatureMin, SoilTemperatureMax, SunlightNeededMin, SunlightNeededMax, WaterNeededMin, WaterNeededMax, PHMin, PHMax \n&quot; +
&quot;    FROM CropSpecies CROSS JOIN CropVariety CROSS JOIN CropType \n&quot; +
&quot;    ON CropSpecies.SpeciesNumber = CropVariety.SpeciesNumber \n&quot; +
&quot;        AND CropType.CropTypeNumber = CropVariety.CropTypeNumber \n&quot; +
&quot;    GROUP BY SpeciesName, VarietyName, CropTypeName, SoilTemperatureMin, SoilTemperatureMax, SunlightNeededMin, SunlightNeededMax, WaterNeededMin, WaterNeededMax, PHMin, PHMax&quot;);
while (cropTable.next()) {
String speciesName = cropTable.getString(&quot;SpeciesName&quot;);
String varietyName = cropTable.getString(&quot;VarietyName&quot;);
String cropTypeName = cropTable.getString(&quot;CropTypeName&quot;);
String soilTemperatureMin = cropTable.getString(&quot;SoilTemperatureMin&quot;);
String soilTemperatureMax = cropTable.getString(&quot;SoilTemperatureMax&quot;);
String sunlightNeededMin = cropTable.getString(&quot;SunlightNeededMin&quot;);
String sunlightNeededMax = cropTable.getString(&quot;SunlightNeededMax&quot;);
String waterNeededMin = cropTable.getString(&quot;WaterNeededMin&quot;);
String waterNeededMax = cropTable.getString(&quot;WaterNeededMax&quot;);
String pHMin = cropTable.getString(&quot;PHMin&quot;);
String pHMax = cropTable.getString(&quot;PHMax&quot;);
String soilTemperatureRange = soilTemperatureMin+ &quot; - &quot; + soilTemperatureMax + &quot; F&quot;;
String sunlightNeededRange = sunlightNeededMin + &quot; - &quot; + sunlightNeededMax + &quot; Hours&quot;;
String waterNeededRange = waterNeededMin + &quot; - &quot; + waterNeededMax + &quot; inches&quot;;
String pHRange = pHMin + &quot; - &quot; + pHMax;
CropRow cropRow = new CropRow(speciesName, varietyName, cropTypeName, soilTemperatureRange, sunlightNeededRange, waterNeededRange, pHRange);
data.add(cropRow);
}
this.speciesCol.setCellValueFactory(
new PropertyValueFactory&lt;CropRow, String&gt;(&quot;speciesName&quot;));
this.varietyCol.setCellValueFactory(
new PropertyValueFactory&lt;CropRow, String&gt;(&quot;varietyName&quot;));
this.typeCol.setCellValueFactory(
new PropertyValueFactory&lt;CropRow, String&gt;(&quot;cropTypeName&quot;));
this.soilTempCol.setCellValueFactory(
new PropertyValueFactory&lt;CropRow, String&gt;(&quot;soilTemperature&quot;));
this.weeklyWaterCol.setCellValueFactory(
new PropertyValueFactory&lt;CropRow, String&gt;(&quot;waterNeeded&quot;));
this.sunlightCol.setCellValueFactory(
new PropertyValueFactory&lt;CropRow, String&gt;(&quot;sunlightNeeded&quot;));
this.pHCol.setCellValueFactory(
new PropertyValueFactory&lt;CropRow, String&gt;(&quot;pH&quot;));
for(CropRow cr : data) {
System.out.println(cr.printCrop());
}
this.cropTable.setItems(data);
Database.disconnect();
}
@FXML
private void addCropAction() { }
@FXML
private void deleteCropAction() { }
/**
* Returns to the title screen when the returnToMain Button is clicked.
*/
@FXML
private void returnToMainAction() {
GardenersApp gApp = GetG_app();
gApp.switchScreen(Page.TITLE);
}
/**
* Holds data on a row in the cropTable TableView object.
*/
private static class CropRow {
private final SimpleStringProperty speciesName;
private final SimpleStringProperty varietyName;
private final SimpleStringProperty cropTypeName;
private final SimpleStringProperty soilTemperature;
private final SimpleStringProperty waterNeeded;
private final SimpleStringProperty sunlightNeeded;
private final SimpleStringProperty pH;
private CropRow(String species, String variety, String cropType, String soilTemp, String water, String sunlight, String pH) {
this.speciesName = new SimpleStringProperty(species);
this.varietyName = new SimpleStringProperty(variety);
this.cropTypeName = new SimpleStringProperty(cropType);
this.soilTemperature = new SimpleStringProperty(soilTemp);
this.waterNeeded = new SimpleStringProperty(water);
this.sunlightNeeded = new SimpleStringProperty(sunlight);
this.pH = new SimpleStringProperty(pH);
}
public String getSpeciesName() {
return speciesName.get();
}
public void setSpeciesName(String speciesName) {
this.speciesName.set(speciesName);
}
public String getVarietyName() {
return varietyName.get();
}
public void setVarietyName(String varietyName) {
this.varietyName.set(varietyName);
}
public String getCropTypeName() {
return cropTypeName.get();
}
public void setCropTypeName(String cropTypeName) {
this.cropTypeName.set(cropTypeName);
}
public String getSoilTemperature() {
return soilTemperature.get();
}
public void setSoilTemperature(String soilTemperature) {
this.soilTemperature.set(soilTemperature);
}
public String getWaterNeeded() {
return waterNeeded.get();
}
public void setWaterNeeded(String waterNeeded) {
this.waterNeeded.set(waterNeeded);
}
public String getSunlightNeeded() {
return sunlightNeeded.get();
}
public void setSunlightNeeded(String sunlightNeeded) {
this.sunlightNeeded.set(sunlightNeeded);
}
public String getpH() {
return pH.get();
}
public void setpH(String pH) {
this.pH.set(pH);
}
public String printCrop() {
return this.speciesName.get() + &quot; | &quot; + this.cropTypeName.get() + &quot; | &quot; + this.varietyName.get();
}
}
}

The FXML File:

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;?import javafx.geometry.Insets?&gt;
&lt;?import javafx.scene.control.Button?&gt;
&lt;?import javafx.scene.control.TableColumn?&gt;
&lt;?import javafx.scene.control.TableView?&gt;
&lt;?import javafx.scene.layout.AnchorPane?&gt;
&lt;?import javafx.scene.layout.HBox?&gt;
&lt;?import javafx.scene.layout.VBox?&gt;
&lt;AnchorPane maxHeight=&quot;-Infinity&quot; maxWidth=&quot;-Infinity&quot; minHeight=&quot;-Infinity&quot; minWidth=&quot;-Infinity&quot; prefHeight=&quot;720.0&quot; prefWidth=&quot;1280.0&quot; xmlns=&quot;http://javafx.com/javafx/11.0.1&quot; xmlns:fx=&quot;http://javafx.com/fxml/1&quot; fx:controller=&quot;main.gui.CropsController&quot;&gt;
&lt;children&gt;
&lt;HBox prefHeight=&quot;720.0&quot; prefWidth=&quot;1280.0&quot;&gt;
&lt;children&gt;
&lt;VBox prefHeight=&quot;720.0&quot; prefWidth=&quot;200.0&quot; spacing=&quot;15.0&quot;&gt;
&lt;padding&gt;
&lt;Insets bottom=&quot;25.0&quot; left=&quot;25.0&quot; top=&quot;25.0&quot; /&gt;
&lt;/padding&gt;
&lt;children&gt;
&lt;Button fx:id=&quot;addCrop&quot; mnemonicParsing=&quot;false&quot; onAction=&quot;#addCropAction&quot; text=&quot;Add Crop&quot; /&gt;
&lt;Button fx:id=&quot;deleteCrop&quot; layoutX=&quot;35.0&quot; layoutY=&quot;35.0&quot; mnemonicParsing=&quot;false&quot; onAction=&quot;#deleteCropAction&quot; text=&quot;Delete Crop&quot; /&gt;
&lt;Button fx:id=&quot;returnToMain&quot; layoutX=&quot;35.0&quot; layoutY=&quot;75.0&quot; mnemonicParsing=&quot;false&quot; onAction=&quot;#returnToMainAction&quot; text=&quot;Return to Main&quot; /&gt;
&lt;/children&gt;
&lt;/VBox&gt;
&lt;TableView fx:id=&quot;cropTable&quot; prefHeight=&quot;200.0&quot; prefWidth=&quot;1080.0&quot;&gt;
&lt;columns&gt;
&lt;TableColumn fx:id=&quot;speciesCol&quot; prefWidth=&quot;102.0&quot; text=&quot;Species&quot; /&gt;
&lt;TableColumn fx:id=&quot;varietyCol&quot; prefWidth=&quot;168.0&quot; text=&quot;Variety&quot; /&gt;
&lt;TableColumn fx:id=&quot;typeCol&quot; prefWidth=&quot;115.0&quot; text=&quot;Type&quot; /&gt;
&lt;TableColumn fx:id=&quot;soilTempCol&quot; prefWidth=&quot;119.0&quot; text=&quot;Soil Temperature&quot; /&gt;
&lt;TableColumn fx:id=&quot;weeklyWaterCol&quot; prefWidth=&quot;118.0&quot; text=&quot;Weekly Water&quot; /&gt;
&lt;TableColumn fx:id=&quot;sunlightCol&quot; prefWidth=&quot;98.0&quot; text=&quot;Sunlight&quot; /&gt;
&lt;TableColumn fx:id=&quot;pHCol&quot; minWidth=&quot;0.0&quot; prefWidth=&quot;69.0&quot; text=&quot;pH&quot; /&gt;
&lt;/columns&gt;
&lt;HBox.margin&gt;
&lt;Insets /&gt;
&lt;/HBox.margin&gt;
&lt;/TableView&gt;
&lt;/children&gt;
&lt;/HBox&gt;
&lt;/children&gt;
&lt;/AnchorPane&gt;

答案1

得分: 1

PropertyValueFactory文档中的示例:

使用此类的示例是:

TableColumn<Person, String> firstNameCol = new TableColumn<Person, String>("First Name");
firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));

在这个示例中,PersonTableView项列表的类类型。Person必须声明为public。

您的代码无法工作,因为您的模型类CropRow声明为private:如果要使用PropertyValueFactory,您需要将其声明为public

请注意,PropertyValueFactory是一个旧的类,仅为了避免在Java 8之前创建单元格值工厂时需要大量样板代码而引入的。它有一些缺点(最重要的是依赖将字符串映射到方法名称,这在编译时无法检查;但还有其他缺点,正如您已经发现的,需要完全访问模型类)。

随着Java 8及更高版本中编译时类型推断和Lambda表达式的改进,实际上不再需要PropertyValueFactory

根据上面链接的文档,将“属性访问器方法”添加到您的模型类(即使您仍然使用PropertyValueFactory,这也是一个好主意):

private static class CropRow {

    private final StringProperty speciesName;

    private CropRow(String species /* etc... */) {

        this.speciesName = new SimpleStringProperty(species);
        // ...

    }

    public StringProperty speciesNameProperty() {
        return speciesName;
    }

    public final String getSpeciesName() {
        return speciesNameProperty().get();
    }

    public final void setSpeciesName(String speciesName) {
        speciesNameProperty().set(speciesName);
    }

    // 类似地处理其他属性...

}

然后您只需要:

this.speciesCol.setCellValueFactory(cellData ->
    cellData.getValue().speciesNameProperty());
英文:

From the documentation for PropertyValueFactory::

> An example of how to use this class is:
>
> TableColumn<Person,String> firstNameCol = new TableColumn<Person,String>("First Name");
> firstNameCol.setCellValueFactory(new PropertyValueFactory<Person,String>("firstName"));
>
> In this example,
> Person is the class type of the TableView items list. The class Person
> must be declared public.

(my emphasis).

Your code fails to work because your model class CropRow is declared private: you need to make it public if you want to use a PropertyValueFactory.

Note that PropertyValueFactory is something of a legacy class that was introduced solely to avoid a lot of boilerplate code that would have been necessary to create a cell value factory prior to Java 8. It has several disadvantages (most importantly, relying on mapping strings to method names, which can't be checked at compile time; but also, as you've discovered, needing full access to the model class).

With the advent of improved compile-time type inference and lambda expressions in Java 8 and later, PropertyValueFactory is really not needed any more.

Add "property accessor methods" to your model class (according to the documentation linked above, this is a good idea even if you still use PropertyValueFactory):

private static class CropRow {
private final StringProperty speciesName;
private CropRow(String species /* etc... */) {
this.speciesName = new SimpleStringProperty(species);
// ...
}
public StringProperty speciesNameProperty() {
return speciesName ;
}
public final String getSpeciesName() {
return speciesNameProperty.get();
}
public final void setSpeciesName(String speciesName) {
speciesNameProperty().set(speciesName);
}
// similarly for other properties...
}

and then you just need:

this.speciesCol.setCellValueFactory(cellData -&gt; 
cellData.getValue().speciesNameProperty());

huangapple
  • 本文由 发表于 2020年8月3日 06:25:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/63221602.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定