Javafx - 应用程序 class 可以成为控制器 class
Javafx - Can application class be the controller class
我目前正在自学 JavaFX,我采用了一个简单的示例程序,该程序对视图进行了硬编码,并将其转换为使用 FXML 的程序(主要是为了我可以使用 SceneBuilder 构建 UIs)。我没有编写单独的控制器 class,而是使用应用程序 class(因此有 1 个 Java 文件和 1 个 FXML 文件)。我没有使用 initialize()
方法,因为它是线性流程(显示 UI,填充字段,等待输入)。弹出视图,但随后应用程序出错,因为 none 控件映射到适当的变量(因此对于 @FXML TableView<...> table
,table
是 null
)。
然而,我放入了一个initialize()
方法进行调试,控件在initialize()
中注入,然后return在initialize()
退出时为null。
所以问题是,JavaFX 是否将应用程序 class 的新实例实例化为单独的控制器 class?这可以解释为什么变量超出范围。还是其他原因(例如,控件仅在从 JavaFX 操作回调时注入)?
如果您已将应用程序 class 定义为 FXML 文件中的控制器,如果我没记错的话,JavaFX 将创建应用程序的新实例 class 并使用新实例作为控制器。因此,您现有的应用程序 class 仍然具有 table.
的空值
但是您可以在您的应用程序中以编程方式定义控制器 class 以使用您自己的实例:
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("example.fxml"));
fxmlLoader.setController(this);
Parent root = (Parent)fxmlLoader.load();
FXMLLoader
的默认行为是创建控制器的新实例 class 并将该实例用作控制器。
具体来说,FXMLLoader
会做类似的事情:
- 读取根 FXML 元素。
- 如果根 FXML 元素具有
fx:controller
属性,则
- 如果控制器已经存在,则抛出异常,否则创建指定class1的实例并将其设置为控制器
- 继续解析 FXML 文件。如果元素具有
fx:id
属性,并且存在控制器(通过任何机制),则将这些字段注入控制器。同样将事件处理程序注册为对控制器实例中方法的调用。
- 在控制器上调用
initialize()
,如果控制器存在并且它有这样的方法。
所以,你问的问题:
Can application class be the controller class
是的,但这可能是个糟糕的主意。如果您使用 fx:controller
简单地将 Application
subclass 指定为控制器 class,则会创建 Application
subclass 的第二个实例, @FXML
-注释字段被注入到第二个实例中,并且 initialize()
方法被调用到第二个实例中。显然,@FXML
字段永远不会在调用 start(...)
的实例上初始化,并且永远不会在该实例上调用 initialize()
方法。
你的意思可能是:
Can the application class instance created at launch be used as the controller?
这个问题的答案也是肯定的,而且,除了您打算立即丢弃的非常小的演示程序之外,这也可能是一个非常糟糕的主意。您可以通过
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
属性。
这样做的问题是你没有分离,也没有灵活性。 (例如,如果您在某处创建 FXML 文件中定义的视图的第二个实例,您最终会得到第二个 Application
subclass 实例,这充其量是违反直觉的(一个应用程序有两个 Application
实例...))
所以我提倡基本上在每种情况下都为控制器使用单独的 class。 Application
subclass 应该包含最少的代码并且应该只用于启动应用程序。
1 这一步其实稍微复杂一点。如果在 fx:controller
属性中指定了 class,并且控制器不存在,则 FXMLLoader
会检查 controllerFactory
。如果存在,则将控制器设置为将指定的 Class
传递给 controllerFactory
的 call()
方法的结果,否则通过调用 newInstance()
指定 class(有效地调用其无参数构造函数)。
我目前正在自学 JavaFX,我采用了一个简单的示例程序,该程序对视图进行了硬编码,并将其转换为使用 FXML 的程序(主要是为了我可以使用 SceneBuilder 构建 UIs)。我没有编写单独的控制器 class,而是使用应用程序 class(因此有 1 个 Java 文件和 1 个 FXML 文件)。我没有使用 initialize()
方法,因为它是线性流程(显示 UI,填充字段,等待输入)。弹出视图,但随后应用程序出错,因为 none 控件映射到适当的变量(因此对于 @FXML TableView<...> table
,table
是 null
)。
然而,我放入了一个initialize()
方法进行调试,控件在initialize()
中注入,然后return在initialize()
退出时为null。
所以问题是,JavaFX 是否将应用程序 class 的新实例实例化为单独的控制器 class?这可以解释为什么变量超出范围。还是其他原因(例如,控件仅在从 JavaFX 操作回调时注入)?
如果您已将应用程序 class 定义为 FXML 文件中的控制器,如果我没记错的话,JavaFX 将创建应用程序的新实例 class 并使用新实例作为控制器。因此,您现有的应用程序 class 仍然具有 table.
的空值但是您可以在您的应用程序中以编程方式定义控制器 class 以使用您自己的实例:
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("example.fxml"));
fxmlLoader.setController(this);
Parent root = (Parent)fxmlLoader.load();
FXMLLoader
的默认行为是创建控制器的新实例 class 并将该实例用作控制器。
具体来说,FXMLLoader
会做类似的事情:
- 读取根 FXML 元素。
- 如果根 FXML 元素具有
fx:controller
属性,则- 如果控制器已经存在,则抛出异常,否则创建指定class1的实例并将其设置为控制器
- 如果根 FXML 元素具有
- 继续解析 FXML 文件。如果元素具有
fx:id
属性,并且存在控制器(通过任何机制),则将这些字段注入控制器。同样将事件处理程序注册为对控制器实例中方法的调用。 - 在控制器上调用
initialize()
,如果控制器存在并且它有这样的方法。
所以,你问的问题:
Can application class be the controller class
是的,但这可能是个糟糕的主意。如果您使用 fx:controller
简单地将 Application
subclass 指定为控制器 class,则会创建 Application
subclass 的第二个实例, @FXML
-注释字段被注入到第二个实例中,并且 initialize()
方法被调用到第二个实例中。显然,@FXML
字段永远不会在调用 start(...)
的实例上初始化,并且永远不会在该实例上调用 initialize()
方法。
你的意思可能是:
Can the application class instance created at launch be used as the controller?
这个问题的答案也是肯定的,而且,除了您打算立即丢弃的非常小的演示程序之外,这也可能是一个非常糟糕的主意。您可以通过
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
属性。
这样做的问题是你没有分离,也没有灵活性。 (例如,如果您在某处创建 FXML 文件中定义的视图的第二个实例,您最终会得到第二个 Application
subclass 实例,这充其量是违反直觉的(一个应用程序有两个 Application
实例...))
所以我提倡基本上在每种情况下都为控制器使用单独的 class。 Application
subclass 应该包含最少的代码并且应该只用于启动应用程序。
1 这一步其实稍微复杂一点。如果在 fx:controller
属性中指定了 class,并且控制器不存在,则 FXMLLoader
会检查 controllerFactory
。如果存在,则将控制器设置为将指定的 Class
传递给 controllerFactory
的 call()
方法的结果,否则通过调用 newInstance()
指定 class(有效地调用其无参数构造函数)。