javafx 8兼容性问题 - FXML静态字段

时间:2021-08-19 13:57:42

I have designed a javafx application which works fine in jdk 7. When I try to run it in java 8 I am getting the below exceptions:

我设计了一个javkx应用程序在jdk 7中工作正常。当我尝试在java 8中运行它时,我得到以下异常:

javafx.fxml.LoadException: at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2617)    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2595)    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3230)    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3191)    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3164)    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3140)    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3132)Exception in thread "JavaFX Application Thread" java.lang.NullPointerException: Root cannot be null    at javafx.scene.Scene.<init>(Scene.java:364)    at javafx.scene.Scene.<init>(Scene.java:232)        at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)    at javafx.event.Event.fireEvent(Event.java:204)    at javafx.concurrent.EventHelper.fireEvent(EventHelper.java:219)    at javafx.concurrent.Task.fireEvent(Task.java:1357)    at javafx.concurrent.Task.setState(Task.java:720)    at javafx.concurrent.Task$TaskCallable$2.run(Task.java:1438)    at com.sun.javafx.application.PlatformImpl$6$1.run(PlatformImpl.java:301)    at com.sun.javafx.application.PlatformImpl$6$1.run(PlatformImpl.java:298)    at java.security.AccessController.doPrivileged(Native Method)    at com.sun.javafx.application.PlatformImpl$6.run(PlatformImpl.java:298)    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)    at com.sun.glass.ui.win.WinApplication.access$300(WinApplication.java:39)    at com.sun.glass.ui.win.WinApplication$4$1.run(WinApplication.java:112)    at java.lang.Thread.run(Thread.java:744)

I found out the reason for this is in the initialize method of the controller class I am not able to use the inbuilt methods in any static component. (For example: staticMyTextField.setText() is causing the problem in java 8 but not in java 7). I am not able to find out anything documented regarding this in the javafx guides. Can someone please provide some ideas on why this is causing an issue in Java 8? And also share documents related to this if any.

我发现原因是在控制器类的initialize方法中我无法在任何静态组件中使用内置方法。 (例如:staticMyTextField.setText()导致java 8中的问题,但不会导致java 7中的问题)。我无法在javafx指南中找到有关此内容的任何文档。有人可以提供一些关于为什么这会导致Java 8问题的想法吗?并且还共享与此相关的文档(如果有的话)。

1 个解决方案

#1


It sounds like you are trying to inject a TextField into a static field. Something like

听起来您正在尝试将TextField注入静态字段。就像是

@FXMLprivate static TextField myTextField ;

This apparently worked in JavaFX 2.2. It doesn't work in JavaFX 8. Since no official documentation ever supported this use, it's doesn't really violate backward compatibility, though in fairness the documentation on exactly what the FXMLLoader does is pretty woeful.

这显然适用于JavaFX 2.2。它在JavaFX 8中不起作用。由于没有官方文档曾经支持过这种用法,所以它并没有真正违反向后兼容性,尽管公平地说明FXMLLoader所做的文件非常糟糕。

It doesn't really make much sense to make @FXML-injected fields static. When you load an FXML file, it creates new objects for each of the elements in the FXML file. A new controller instance is associated with each call to FXMLLoader.load(...) and the fields in that controller instance are injected with the corresponding objects created for the FXML elements. So the injected fields are necessarily specific to the controller instance. If you had a static injected fields in the controller, and you loaded the same FXML file twice and displayed it twice in the UI, then you would have no way of referencing both sets of controls.

将@ FXML注入的字段静态化并没有多大意义。加载FXML文件时,它会为FXML文件中的每个元素创建新对象。每个对FXMLLoader.load(...)的调用都会关联一个新的控制器实例,并且该控制器实例中的字段将注入为FXML元素创建的相应对象。因此,注入的字段必须特定于控制器实例。如果您在控制器中有一个静态注入字段,并且您加载了相同的FXML文件两次并在UI中显示两次,那么您将无法引用这两组控件。

Update: Response to question in comments

更新:回复评论中的问题

In particular, don't use static fields just to enable them to be accessible from outside the class. A static field has a single value belonging to the class, instead of a value for each instance of the class, and the decision to make fields static should only be made if that makes sense. In other words, static defines scope, not accessibility. To allow access to instance data, you just have to have a reference to the instance. The FXMLLoader has a getController() method that allows you to retrieve a reference to the controller.

特别是,不要仅使用静态字段来使它们可以从类外部访问。静态字段具有属于该类的单个值,而不是该类的每个实例的值,并且只有在有意义的情况下才应该将字段设置为静态。换句话说,static定义范围,而不是可访问性。要允许访问实例数据,您只需要引用该实例。 FXMLLoader有一个getController()方法,允许您检索对控制器的引用。

A related point: it's also not a good idea to expose the UI controls from the controller. You should instead expose the data. For example, instead of defining a getTextField() method in the controller, instead define a textProperty() method that returns a StringProperty representing the contents of the TextField. The reason for this is that when your boss comes to the office and tells you he wants the TextField to be replaced by a TextArea, or a ComboBox<String>, or some other control, then it's going to be a lot harder if classes outside of the controller are using your TextField. The structure of the data represented by your controller is much less likely to change than the implementation of how that data is presented to the user.

一个相关的观点:从控制器公开UI控件也不是一个好主意。您应该改为公开数据。例如,不是在控制器中定义getTextField()方法,而是定义一个textProperty()方法,该方法返回表示TextField内容的StringProperty。这样做的原因是,当你的老板来到办公室并告诉你他希望TextField被TextArea,ComboBox 或其他控件替换时,如果外面的课程会更难控制器正在使用您的TextField。由控制器表示的数据结构比实现向用户呈现数据的方式更不可能发生变化。

For some examples

举个例子

#1


It sounds like you are trying to inject a TextField into a static field. Something like

听起来您正在尝试将TextField注入静态字段。就像是

@FXMLprivate static TextField myTextField ;

This apparently worked in JavaFX 2.2. It doesn't work in JavaFX 8. Since no official documentation ever supported this use, it's doesn't really violate backward compatibility, though in fairness the documentation on exactly what the FXMLLoader does is pretty woeful.

这显然适用于JavaFX 2.2。它在JavaFX 8中不起作用。由于没有官方文档曾经支持过这种用法,所以它并没有真正违反向后兼容性,尽管公平地说明FXMLLoader所做的文件非常糟糕。

It doesn't really make much sense to make @FXML-injected fields static. When you load an FXML file, it creates new objects for each of the elements in the FXML file. A new controller instance is associated with each call to FXMLLoader.load(...) and the fields in that controller instance are injected with the corresponding objects created for the FXML elements. So the injected fields are necessarily specific to the controller instance. If you had a static injected fields in the controller, and you loaded the same FXML file twice and displayed it twice in the UI, then you would have no way of referencing both sets of controls.

将@ FXML注入的字段静态化并没有多大意义。加载FXML文件时,它会为FXML文件中的每个元素创建新对象。每个对FXMLLoader.load(...)的调用都会关联一个新的控制器实例,并且该控制器实例中的字段将注入为FXML元素创建的相应对象。因此,注入的字段必须特定于控制器实例。如果您在控制器中有一个静态注入字段,并且您加载了相同的FXML文件两次并在UI中显示两次,那么您将无法引用这两组控件。

Update: Response to question in comments

更新:回复评论中的问题

In particular, don't use static fields just to enable them to be accessible from outside the class. A static field has a single value belonging to the class, instead of a value for each instance of the class, and the decision to make fields static should only be made if that makes sense. In other words, static defines scope, not accessibility. To allow access to instance data, you just have to have a reference to the instance. The FXMLLoader has a getController() method that allows you to retrieve a reference to the controller.

特别是,不要仅使用静态字段来使它们可以从类外部访问。静态字段具有属于该类的单个值,而不是该类的每个实例的值,并且只有在有意义的情况下才应该将字段设置为静态。换句话说,static定义范围,而不是可访问性。要允许访问实例数据,您只需要引用该实例。 FXMLLoader有一个getController()方法,允许您检索对控制器的引用。

A related point: it's also not a good idea to expose the UI controls from the controller. You should instead expose the data. For example, instead of defining a getTextField() method in the controller, instead define a textProperty() method that returns a StringProperty representing the contents of the TextField. The reason for this is that when your boss comes to the office and tells you he wants the TextField to be replaced by a TextArea, or a ComboBox<String>, or some other control, then it's going to be a lot harder if classes outside of the controller are using your TextField. The structure of the data represented by your controller is much less likely to change than the implementation of how that data is presented to the user.

一个相关的观点:从控制器公开UI控件也不是一个好主意。您应该改为公开数据。例如,不是在控制器中定义getTextField()方法,而是定义一个textProperty()方法,该方法返回表示TextField内容的StringProperty。这样做的原因是,当你的老板来到办公室并告诉你他希望TextField被TextArea,ComboBox 或其他控件替换时,如果外面的课程会更难控制器正在使用您的TextField。由控制器表示的数据结构比实现向用户呈现数据的方式更不可能发生变化。

For some examples

举个例子