问题描述
我目前正在自学 JavaFX,我采用了一个简单的示例程序,该程序对视图进行了硬编码,并将其转换为使用 FXML 的程序(主要是这样我可以使用 SceneBuilder 来构建 UI).我没有编写单独的控制器类,而是使用应用程序类(因此有 1 个 Java 文件和 1 个 FXML 文件).我没有使用 initialize()
方法,因为它是一个线性流程(显示 UI、填充字段、等待输入).视图弹出,但随后应用程序出错,因为没有任何控件映射到适当的变量(因此对于 @FXML TableView table
、table
是 null
).
I'm currently teaching myself JavaFX, and I've taken a simple example program that's hardcoded the view and am turning it into one that uses FXML (mostly so I can use SceneBuilder for building UIs). Rather than writing a separate controller class, I'm using the application class (so there's 1 Java file and 1 FXML file). I'm not using an initialize()
method as it's a linear flow (display the UI, populate the fields, wait for input). The view pops up, but then the app errors out as none of the controls are mapped to the appropriate variables (so for @FXML TableView<...> table
, table
is null
).
然而,我在调试时放入了一个initialize()
方法,在initialize()
中注入了控件,然后在initialize时返回null()
退出.
However, I put in an initialize()
method for debugging, the controls are injected while in initialize()
, and then return to null when initialize()
exits.
那么问题是,JavaFX 是否将应用程序类的新实例实例化为单独的控制器类?这将解释为什么变量超出范围.或者是其他什么(例如,仅在从 JavaFX 操作回调时才注入控件)?
So the question is, does JavaFX instantiate a new instance of the application class as a separate controller class? This would explain why the variable are going out of scope. Or is it something else (e.g. the controls are injected only when being called back from JavaFX actions)?
推荐答案
FXMLLoader
的默认行为是创建控制器类的新实例并将该实例用作控制器.
The default behavior of the FXMLLoader
is to create a new instance of the controller class and use that instance as the controller.
>
具体来说,FXMLLoader
执行以下操作:
- 读取根 FXML 元素.
- 如果根 FXML 元素具有
fx:controller
属性,则- 如果控制器已经存在,则抛出异常,否则创建指定类的实例并将其设置为控制器
- Read the root FXML element.
- If the root FXML element has a
fx:controller
attribute, then- If a controller already exists, throw an exception, otherwise create an instance of the specified class and set that as the controller
所以,你问的问题:
应用类可以作为控制器类吗
是的,但这可能是一个糟糕的主意.如果您只是使用
fx:controller
将Application
子类指定为控制器类,则会创建Application
子类的第二个实例,@FXML
注释的字段被注入到第二个实例上,并在第二个实例上调用initialize()
方法.显然,@FXML
字段从未在调用start(...)
的实例上初始化,并且initialize()
永远不会在该实例上调用方法.Yes, but it's probably a terrible idea. If you simply specify the
Application
subclass as the controller class usingfx:controller
, then a second instance of theApplication
subclass is created,@FXML
-annotated fields are injected on that second instance, and theinitialize()
method is invoked on that second instance. Obviously, the@FXML
-fields are never initialized on the instance on whichstart(...)
is invoked, and theinitialize()
method is never invoked on that instance.您可能要问的问题是:
启动时创建的应用类实例可以用作控制器吗?
这个问题的答案也是肯定的,而且,除了您打算立即丢弃的非常小的演示程序之外,这也可能是一个非常糟糕的主意.你会这样做
The answer to this is also yes, and, aside from very small demo programs you intend to immediately discard, it's also probably a very bad idea. You would do this by
public class MyApp extends Application { @FXML private Node someNode ; public void initialize() { // do something with someNode } @Override public void start(Stage primaryStage) throws Exception { FXMLLoader loader = new FXMLLoader(getClass().getResource("/path/to/fxml/file.fxml")); loader.setController(this); Parent root = loader.load(); primaryStage.setScene(new Scene(root)); primaryStage.show(); } }
请注意,要使用此代码,您的 FXML 文件不得具有
fx:controller
属性.Note that to use this code, your FXML file must not have a
fx:controller
attribute.这样做的问题是你没有分离和灵活性.(例如,如果您在某处创建 FXML 文件中定义的视图的第二个实例,您最终会得到第二个
Application
子类实例,这充其量是违反直觉的(一个应用程序有两个Application
实例...))The problem with this is that you have no separation and no flexibility. (E.g. if you create a second instance of the view defined in your FXML file somewhere, you end up with a second
Application
subclass instance, which is at best counterintuitive (one application with twoApplication
instances...).)所以我提倡基本上在每种情况下都为控制器使用一个单独的类.
Application
子类应该包含最少的代码,并且应该只用于启动应用程序.So I would advocate using a separate class for the controller in basically every case. The
Application
subclass should contain minimal code and should be used only for starting the application.这一步其实稍微复杂一些.如果在
fx:controller
属性中指定了一个类,并且不存在控制器,则FXMLLoader
会检查controllerFactory
.如果存在,则将控制器设置为将指定的Class
传递给controllerFactory
的call()
方法的结果,否则为通过在指定的类上调用newInstance()
(有效调用其无参数构造函数)来创建.This step is actually a little more complex. If a class is specified in the
fx:controller
attribute, and no controller already exists, theFXMLLoader
checks for acontrollerFactory
. If one exists, then the controller is set as the result of passing the specifiedClass
to thecontrollerFactory
'scall()
method, otherwise it is created by callingnewInstance()
on the specified class (effectively calling its no-argument constructor).这篇关于Javafx - 应用程序类可以作为控制器类吗的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
- If the root FXML element has a
- 如果根 FXML 元素具有