使用Properties时无法在JavaFX TableView中显示数据

时间:2018-04-17 17:33:14

标签: javafx properties tableview

我已经阅读了几个关于使用Properties在JavaFX TableView中显示数据的线程,我可以使所有这些工作正常。到目前为止,我一直在没有问题的TableView中显示数据而没有问题。但是,继续前进我认为我需要开始使用Properties,特别是因为我希望我的TableView可以编辑。我怀疑问题是因为我看过的所有例子都使用一个对象来显示1行数据,而在我的模型中我每个单元格有一个对象(一个Field对象),但我想不出为什么这应该是一个问题。

以下是我的Field类的非常简化的版本,足以进行此测试,但还不足以显示其实际用途。我还有一个测试类TableViewDemo。任何有关这个令人厌烦的问题的帮助将非常感激。要么是愚蠢的错误,要么是我设计的根本问题。

package javafxtest;
import java.sql.SQLException;
import javafx.beans.property.SimpleObjectProperty;

public class Field {

    private final SimpleObjectProperty objectValue; //All the data is stored as an object, then cast to appropriate data type

    public Field(Object objValue) throws SQLException {
        if (objValue == null) {
            throw new SQLException("Field value is null");
        }
        this.objectValue = new SimpleObjectProperty(objValue);
    }

    public final String getStringValue() {
        return (String) this.objectValue.get();
    }

    public final int getIntValue() {
        return (Integer) this.objectValue.get();
    }

    public final double getDoubleValue() {
        return (Double) this.objectValue.get();
    }

    public final boolean getBooleanValue() {
        return (Boolean) this.objectValue.get();
    }
}

package javafxtest;

import java.sql.SQLException;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;

public class TableViewDemo extends Application {

    @Override
    public void start(Stage myStage) {
        // Set up the stage
        myStage.setTitle("Demonstrate TableView");
        FlowPane rootNode = new FlowPane(10, 10);
        rootNode.setAlignment(Pos.CENTER);
        Scene myScene = new Scene(rootNode, 600, 450);
        myStage.setScene(myScene);

        //Set up some test data - 4 rows of 4 columns
        ObservableList<Field[]> dataModel = FXCollections.observableArrayList();
        ObservableList<Field[]> dataModel2 = FXCollections.observableArrayList();
        Field[] fields1;
        Field[] fields2;
        Field[] fields3;
        Field[] fields4;
        try {
            fields1 = new Field[4];
            fields1[0] = new Field("R1C1");
            fields1[1] = new Field(25);
            fields1[2] = new Field(100.00);
            fields1[3] = new Field(true);

            fields2 = new Field[4];
            fields2[0] = new Field("R2C1");
            fields2[1] = new Field(30);
            fields2[2] = new Field(150.00);
            fields2[3] = new Field(false);

            fields3 = new Field[4];
            fields3[0] = new Field("R3C1");
            fields3[1] = new Field(397);
            fields3[2] = new Field(1005.99);
            fields3[3] = new Field(true);

            fields4 = new Field[4];
            fields4[0] = new Field("R4C1");
            fields4[1] = new Field(1085);
            fields4[2] = new Field(1.00);
            fields4[3] = new Field(false);

            //Add the data to the dataModel
            dataModel.addAll(fields1, fields2, fields3, fields4);

            //Set up the columns ===== This is where the likely problem is, or at least part of the problem ====
            TableColumn<Field[], String> colString = new TableColumn<>("String");
            colString.setCellValueFactory(new PropertyValueFactory<>("stringValue"));
            colString.setPrefWidth(150);

            TableColumn<Field[], Integer> colInt = new TableColumn<>("int");
            colInt.setCellValueFactory(new PropertyValueFactory<>("intValue"));
            colInt.setPrefWidth(100);

            TableColumn<Field[], Double> colDouble = new TableColumn<>("double");
            colDouble.setCellValueFactory(new PropertyValueFactory<>("doubleValue"));
            colDouble.setPrefWidth(100);

            TableColumn<Field[], Boolean> colBoolean = new TableColumn<>("boolean");
            colBoolean.setCellValueFactory(new PropertyValueFactory<>("booleanValue"));
            colBoolean.setPrefWidth(75);

            //Set up the TableView - it takes an Array of Field objects. Add the data and columns to it
            TableView<Field[]> tableView = new TableView<Field[]>();
            tableView.setItems(dataModel);
            tableView.getColumns().addAll(colString, colInt, colDouble, colBoolean);

            //Print out the datamodel
            dataModel2 = tableView.getItems();
            for (int i = 0; i < dataModel2.size(); i++) {
                Field[] fields = new Field[4];
                fields = dataModel2.get(i);
                for (int j = 0; j < fields.length; j++) {
                    Field field = fields[j];
                    switch (j) {
                        case 0: //Column is a String data type
                            System.out.println(field.getStringValue());
                            break;
                        case 1:  //Column is a int data type
                            System.out.println(field.getIntValue());
                            break;
                        case 2:  //Column is a double data type
                            System.out.println(field.getDoubleValue());
                            break;
                        case 3:  //Column is a boolean data type
                            System.out.println(field.getBooleanValue());
                            break;
                        default:
                            break;
                    }
                }
            }

            // Set the tableView size
            tableView.setPrefWidth(500);
            tableView.setPrefHeight(300);

            rootNode.getChildren().add(tableView);
        } catch (SQLException e) {
            System.out.println(e.getLocalizedMessage());
        }
        myStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

这是我目前使用String值填充单元格的原因:

    final int x = i;
    tableColumn<Field[], String> columnString = (TableColumn<Field[], String>) this.columnModel.get(i);
    columnString.setCellValueFactory((TableColumn.CellDataFeatures<Field[], String> fieldArray) -> {
        Field arrayField = fieldArray.getValue()[x];
        return new ReadOnlyObjectWrapper(arrayField.getStringValue());
    });

1 个答案:

答案 0 :(得分:0)

这根本不是UnZipAssets("gameModLevels.zip", Path.Combine(Application.Context.CacheDir.AbsolutePath, "assets")); 的工作方式。来自documentation

  

如何使用此类的示例如下:

PropertyValueFactory
     

在此示例中,TableColumn<Person,String> firstNameCol = new TableColumn<Person,String>("First Name"); firstNameCol.setCellValueFactory(new PropertyValueFactory<Person,String>("firstName")); Person项列表的类类型。必须将类TableView声明为public。 Person使用构造函数参数PropertyValueFactory来假设"firstName"的公共方法Person没有正式参数且返回类型为firstNameProperty

     

如果存在这样的方法,则调用它,并且还假设返回ObservableValue<String>的实例。返回值用于填充Property<String>。此外,TableCell会在返回值中添加一个观察者,以便TableView可以观察到触发的任何更改,从而导致单元格立即更新。

     

如果不存在此类方法,则TableView会假定PropertyValueFactory具有公共方法PersongetFirstName,且没有正式参数且返回类型为{{1} }。如果存在这样的方法,则调用它,并将其返回值包装在isFirstName中并返回到String

因此,基本上,ReadOnlyObjectWrapper检索表示行的对象,并使用反射来查找方法TableCell,其中PropertyValueFactory是分配给{{1}的属性名称}}。如果不存在这样的方法,则作为后退,它会查找方法xxxProperty(),并将结果(如果存在)包装在xxx中。

在您的情况下,表示该行的值为PropertyValueFactory,即该对象是getXxx()数组。数组没有ReadOnlyObjectWrapperField[]方法(等),因此失败。 (当然,数组的各个元素都有这些方法,但Field不会查看数组元素,它只是查看行的值,即数组本身。)

很难知道如何解决&#34;这个问题,因为没有明显的理由你不想以这种方式处理事情。当然,你可以做到

stringValueProperty()

等等,但在那一点上,你不清楚为什么你不会只定义一个有四个常规字段的类 - 或者甚至只是使用一个getStringValue()来摆脱你的{{ 1}} class - 无论如何。