【问题标题】:PropertyValueFactory error on proper getter from model class来自模型类的正确 getter 上的 PropertyValueFactory 错误
【发布时间】:2019-11-10 04:25:58
【问题描述】:

我正在尝试从 fxml 文件填充示例 javafx TableView。

这是我的控制器方法:

public class TestController implements Initializable {


       @FXML private TableView<user> tableView;
        @FXML private TableColumn<user, String> UserId;
        @FXML private TableColumn<user, String> UserName;


        public void initialize(URL location, ResourceBundle resources) {
            UserId.setCellValueFactory(new PropertyValueFactory<user, String>("userId"));
            UserName.setCellValueFactory(new PropertyValueFactory<user, String>("userName"));


            tableView.getItems().setAll(parseUserList());
        }
        private List<user> parseUserList(){

            List<user> l_u = new ArrayList<user>();

            user u = new user();
            u.setUserId(1);
            u.setUserName("test1");
            l_u.add(u);

            u.setUserId(2);
            u.setUserName("test2");
            l_u.add(u);

            u.setUserId(3);
            u.setUserName("test3");
            l_u.add(u);


            return l_u;
        }

}

和 fxml 文件:

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="369.0" prefWidth="505.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="viewmodel.TestController ">

   <center>
      <TableView fx:id="tableView" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
        <columns>
          <TableColumn prefWidth="75.0" text="UserId" fx:id="UserId"/>
          <TableColumn prefWidth="75.0" text="UserName" fx:id="UserName"/>
        </columns>
      </TableView>
   </center>
</BorderPane>

最后是我的模型:

封装模型;

public class user {
    private int userId;

    public int getUserId() { return this.userId; }
    public void setUserId(int userId) { this.userId = userId; }

    private String userName;

    public String getUserName() { return this.userName; }
    public void setUserName(String userName) { this.userName = userName; }
}

现在当我尝试填充时,它给了我这个错误:

Jun 28, 2019 12:17:35 PM javafx.scene.control.cell.PropertyValueFactory getCellDataReflectively
WARNING: Can not retrieve property 'userId' in PropertyValueFactory: javafx.scene.control.cell.PropertyValueFactory@638db851 with provided class type: class model.user
java.lang.RuntimeException: java.lang.IllegalAccessException: module javafx.base cannot access class model.user (in module JavaFXTest) because module JavaFXTest does not open model to javafx.base
    at javafx.base/com.sun.javafx.property.PropertyReference.get(PropertyReference.java:176)

关于 SO 的一些文章提到,getter 和 setter 属性必须在 get word 后以大写开头,但这并没有解决问题。

【问题讨论】:

    标签: java javafx openjfx


    【解决方案1】:

    这是一个很好的例子“为什么你应该使用Callback而不是PropertyValueFactory。目前我看不出有任何理由使用PVF而不是Callback。

    在这里您可以看到一个缺点,那就是您在运行应用程序之前并没有真正看到自己犯了编码错误。 由于 PVF 与反射一起工作,因此如果您没有正确声明它们,它不会找到适当的字段。 PVF 需要Property-es,正如您在异常中看到的那样 PropertyRefference 是必需的,并且您的 user 类中的所有字段都不是 Propertyes

    可以这样使用,但你必须重写 user 类,如:

    public class User { // use java naming conventions 
        private IntegerProperty userId;
    
        public int getUserId() { return this.userId.get(); }
        public void setUserId(int userId) { this.userId.set(userId); }
        public IntegerProperty userIdProperty() { return this.userId; }
    
        private StringProperty userName;
    
        public String getUserName() { return this.userName.get(); }
        public void setUserName(String userName) { this.userName.set(userName); }
        public StringProperty userNameProperty() {return this.userName; }
    
        public User(int userId,String userName){
             this.userId = new SimpleIntegerProperty(userId);
             this.userName = new SimpleStringProperty(userName);
        }
    }
    

    现在,由于字段是属性,因此 PropertyValueFactory 会找到它们我不建议使用它,因为正如您所见,它可能会导致您在运行之前甚至没有注意到的问题.

    所以不是:

    UserId.setCellValueFactory(new PropertyValueFactory<user, String>("userId"));
    UserName.setCellValueFactory(new PropertyValueFactory<user, String>("userName"));
    

    使用:

    // use java naming conventions (`userId` instead of `UserId`)
    userId.setCellValueFactory(data -> data.getValue().userIdProperty().asString()); 
    // same here user naming convention
    userName.setCellValueFactory(data -> data.getValue().userNameProperty());
    

    我写过几次cmets,但我会在这里再次提及它以使用java命名约定。像 User 而不是 user 和 userId 而不是 UserId 等等......

    【讨论】:

    • 非常感谢您的演练和指导。这很有帮助。这一行只有一件事:userId.setCellValueFactory(data -> data.getValue().userIdProperty());我收到“类型不匹配:无法从 IntegerProperty 转换为 ObservableValue”错误。最好的尊重
    • hmm.. 同意,命名很糟糕(而且往往不是很多痛苦的根源;) - 但这里可能是一个不同的问题:在非在模块化上下文中断中运行时,模块化设置工作得很好(尽管错误消息与 OP Unable to make field private javafx.scene.control.TableView fx.UserController.tableView accessible: module openfx10 does not "opens fx" to module javafx.fxml 略有不同)。知道可能是什么原因吗?
    • 好的,可以通过将opens fx(我的包名是 fx)添加到我的 module.info 来使其运行...看来我必须了解有关模块化设置的更多信息.. ;)跨度>
    • @AhadPorkar 编辑了答案,添加了asString(),它应该可以工作,我没有发现你在列中有String,而不是Integer
    【解决方案2】:

    虽然违反 java 命名约定(始终遵循它们!)和 PropertyValueFactory 的不当使用(如果没有令人信服的理由,请不要使用它!)是通常的嫌疑人,它们似乎不是 OP 中的原因题。

    错误消息表明问题发生在模块化上下文中:

    Jun 28, 2019 12:17:35 PM javafx.scene.control.cell.PropertyValueFactory getCellDataReflectively
    WARNING: Can not retrieve property 'userId' in PropertyValueFactory:   
        javafx.scene.control.cell.PropertyValueFactory@638db851 with provided class type: class model.user
    java.lang.RuntimeException: java.lang.IllegalAccessException: 
        module javafx.base cannot access class model.user (in module JavaFXTest) 
        because module JavaFXTest does not open model to javafx.base
    

    实际上,该示例在非模块化上下文中运行良好,但在模块化时会失败。错误信息准确地告诉我们该怎么做:打开我们的模块(或其中的一部分)以进行反射访问。

    Module-info 打开完整的模块或单个包:

     // complete
     open module JavaFXTest {
       exports ourpackage;
       ...
     }
    
     // parts
     module JavaFXTest {
       exports ourpackage;
       opens ourpackage.model;
     }
    

    【讨论】:

    • 非常感谢您的澄清。我已经从答案和评论部分的“非模块化上下文”中完成了一个并且问题解决了。竖起大拇指:
    • 也许值得一提的是,反射访问也可以提供给特定的模块:opens ourpackage.model to javafx.base;
    • @fabian 好点 :) 当我开始在模块化上下文中进行挖掘时,我的第一个错误是无法访问带有 fxml 注释的私有字段(模型和控制器都在一个包中) - 在在这种情况下,两个目标模块(fxml 和 base)需要打开。一直很懒..
    猜你喜欢
    • 1970-01-01
    • 2013-04-28
    • 1970-01-01
    • 2018-04-21
    • 2013-01-12
    • 2019-05-27
    • 2021-05-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多