我目前正在教自己JavaFX,我已经采用了一个简单的示例程序,该程序硬编码视图并将其转换为使用FXML的程序(主要是因为我可以使用SceneBuilder构建UI)。我没有编写单独的控制器类,而是使用应用程序类(因此有1个Java文件和1个FXML文件)。我没有使用initialize()
方法,因为它是线性流(显示UI,填充字段,等待输入)。视图会弹出,但随后应用程序出错,因为没有任何控件映射到相应的变量(因此对于@FXML TableView<...> table
,table
是null
)。
但是,我使用initialize()
方法进行调试,控件在initialize()
时注入,然后在initialize()
退出时返回null。
所以问题是,JavaFX是否将应用程序类的新实例实例化为单独的控制器类?这可以解释为什么变量超出范围。或者它是否是其他东西(例如,只有在从JavaFX动作回调时才注入控件)?
答案 0 :(得分:8)
FXMLLoader
的默认行为是创建控制器类的新实例,并将该实例用作控制器。
具体来说,FXMLLoader
的确如下:
fx:controller
属性,那么
fx:id
属性,并且存在控制器(通过任何机制),则将这些字段注入控制器。类似地,将事件处理程序注册为对控制器实例中的方法的调用。initialize()
。所以,你问的问题是:
应用程序类可以是控制器类
是的,但这可能是一个糟糕的主意。如果您只是使用Application
将fx:controller
子类指定为控制器类,则会创建Application
子类的第二个实例,@FXML
- 在第二个实例上注入注释字段实例,并在该第二个实例上调用initialize()
方法。显然,@FXML
- 字段永远不会在调用start(...)
的实例上初始化,并且永远不会在该实例上调用initialize()
方法。
你可能想要的问题是:
启动时创建的应用程序类实例是否可以用作控制器?
对此的答案也是肯定的,除了你打算立即丢弃的非常小的演示程序外,它也可能是一个非常糟糕的主意。你可以通过
来做到这一点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
子类实例,这最好是违反直觉的(一个应用程序有两个Application
个实例。 。)。)
所以我主张在每种情况下都为控制器使用一个单独的类。 Application
子类应包含最少的代码,并且只应用于启动应用程序。
1 这一步实际上有点复杂。如果在fx:controller
属性中指定了类,并且没有控制器,则FXMLLoader
会检查controllerFactory
。如果存在,则将控制器设置为将指定的Class
传递给controllerFactory
的{{1}}方法的结果,否则通过调用{{1}创建控制器在指定的类上(有效地调用它的无参数构造函数)。
答案 1 :(得分:0)
如果您已将应用程序类定义为FXML文件中的控制器,那么如果我没记错的话,JavaFX将创建应用程序类的新实例并将新实例用作控制器。因此,您现有的应用程序类对表仍然为null。
但是,您可以在应用程序类中以编程方式定义控制器以使用您自己的实例:
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("example.fxml"));
fxmlLoader.setController(this);
Parent root = (Parent)fxmlLoader.load();