如何 运行 使用 javafx 启动应用程序时的多个不同阶段之一?
How to run one of multiple distinct stages at application startup with javafx?
我刚开始使用 javafx,但在理解如何正确建模以下情况时遇到了一些问题:
理想情况下,我希望有一个 main()
方法可以让我打开一个 LoginDialog
或者如果磁盘上已经有一个 user/password 组合,绕过登录并直接向用户显示MainDialog
。
我的主要问题是,当我 运行 Application.launch()
我应该提交一个 Application
实例,而在实施一个实例时,我无法控制它Stage
object创作,在这里为我创造了一个catch-22。
我可以创建 LoginScene
和 MainScene
,但是我无法控制诸如 Stage
的标题之类的东西。
用 javafx 解决此类问题的通常途径是什么?
谢谢
定义一个单独的 Application
subclass 并将决定是否需要显示登录屏幕的逻辑放在 start()
方法中(启动逻辑的正确位置是恰当命名的 start()
方法,而不是 main
方法):
public class MyApplication extends Application {
private boolean loggedIn ;
@Override
public void start(Stage primaryStage) {
loggedIn = checkLoginFromDisk();
while (! loggedIn) {
FXMLLoader loginLoader = new FXMLLoader(getClass().getResource("path/to/login.fxml"));
Parent loginRoot = loginLoader.load();
LoginController loginController = loginLoader.getController();
Scene loginScene = new Scene(loginRoot);
primaryStage.setScene(loginScene);
primaryStage.setTitle("Login");
primaryStage.showAndWait();
// check login from controller and update loggedIn...
}
FXMLLoader mainLoader = new FXMLLoader(getClass().getResource("path/to/main.fxml"));
Parent mainRoot = mainLoader.load();
Scene mainScene = new Scene(mainRoot);
primaryStage.setScene(mainScene);
primaryStage.setTitle("My Application");
primaryStage.sizeToScene();
primaryStage.show();
}
private boolean checkLoginFromDisk() {
// ... etc
}
// for environments not supporting direct launch of JavaFX:
public static void main(String[] args) {
launch(args);
}
}
如果您不使用 FXML,您只需定义 classes 而不是 FXML 文件 + "login" 和 "main" 的控制器,但结构保持不变:
public class LoginView {
private final GridPane /* for example */ view ;
public LoginView() {
// setup UI, etc...
}
public Pane getView() {
return view ;
}
public boolean checkLogin() {
// etc...
}
}
和
public class MainView {
private BorderPane /* for example */ view ;
public MainView() {
// set up UI etc...
}
public Pane getView() {
return view ;
}
}
然后您的启动方法看起来像
@Override
public void start(Stage primaryStage) {
loggedIn = checkLoginFromDisk();
while (! loggedIn) {
LoginView loginView = new LoginView();
Scene loginScene = new Scene(loginView.getView());
primaryStage.setScene(loginScene);
primaryStage.setTitle("Login");
primaryStage.showAndWait();
loggedIn = loginView.checkLogin();
}
MainView mainView = new MainView();
Scene mainScene = new Scene(mainView.getView());
primaryStage.setScene(mainScene);
primaryStage.setTitle("My Application");
primaryStage.sizeToScene();
primaryStage.show();
}
显然,您可以根据需要以多种不同的方式重构它(重复使用相同的登录 class 或 fxml 实例,为主视图使用不同的阶段,等等)。
请注意,不需要使用传递给 start()
方法的阶段。所以如果你想要独立的 classes 来封装包含登录场景和主场景的舞台,你可以添加以下 classes:
public class LoginStage extends Stage {
private final LoginView loginView ;
public LoginStage() {
loginView = new LoginView();
setScene(new Scene(loginView.getView());
setTitle("Login");
}
public boolean checkLogin() {
return loginView.checkLogin();
}
}
并类似地制作 MainStage
class。 (在基于 FXML 的版本中,LoginStage
持有对 LoginController
的引用,并且只是在构造函数中加载 FXML 而不是实例化 LoginView
class。)然后
public class MyApplication extends Application {
private boolean loggedIn ;
@Override
public void start(Stage ignored) {
loggedIn = checkLoginFromDisk();
while (! loggedIn) {
LoginStage login = new LoginStage();
loginStage.showAndWait();
loggedIn = loginStage.checkLogin();
}
new MainStage().show();
}
// ...
}
这似乎与我要找的非常相似。它遵循jns的建议。
不理想但不可怕:
class LoginScene(stage: Stage) extends Scene(new VBox()) {
val vbox = this.getRoot.asInstanceOf[VBox]
...
}
class MainScene(stage: Stage) extends Scene(new VBox()) {
val vbox = this.getRoot.asInstanceOf[VBox]
...
}
class ApplicationStartup extends Application {
override def start(primaryStage: Stage): Unit = {
val scene = if (...) new LoginScene(primaryStage) else new MainScene(primaryStage)
primaryStage.setScene(scene)
primaryStage.show()
}
}
(代码以 Scala 呈现)
或者,从问题的评论中可以看出,可以忽略 primaryStage
并随意创建我们自己的,这正是我从一开始就想要的:
class MainDialog extends Application {
override def start(primaryStage: Stage): Unit = {
val newStage = new Stage {
setTitle("abcdef")
setScene(new Scene(new Button("Hello World")))
}
newStage.show()
}
}
我刚开始使用 javafx,但在理解如何正确建模以下情况时遇到了一些问题:
理想情况下,我希望有一个 main()
方法可以让我打开一个 LoginDialog
或者如果磁盘上已经有一个 user/password 组合,绕过登录并直接向用户显示MainDialog
。
我的主要问题是,当我 运行 Application.launch()
我应该提交一个 Application
实例,而在实施一个实例时,我无法控制它Stage
object创作,在这里为我创造了一个catch-22。
我可以创建 LoginScene
和 MainScene
,但是我无法控制诸如 Stage
的标题之类的东西。
用 javafx 解决此类问题的通常途径是什么?
谢谢
定义一个单独的 Application
subclass 并将决定是否需要显示登录屏幕的逻辑放在 start()
方法中(启动逻辑的正确位置是恰当命名的 start()
方法,而不是 main
方法):
public class MyApplication extends Application {
private boolean loggedIn ;
@Override
public void start(Stage primaryStage) {
loggedIn = checkLoginFromDisk();
while (! loggedIn) {
FXMLLoader loginLoader = new FXMLLoader(getClass().getResource("path/to/login.fxml"));
Parent loginRoot = loginLoader.load();
LoginController loginController = loginLoader.getController();
Scene loginScene = new Scene(loginRoot);
primaryStage.setScene(loginScene);
primaryStage.setTitle("Login");
primaryStage.showAndWait();
// check login from controller and update loggedIn...
}
FXMLLoader mainLoader = new FXMLLoader(getClass().getResource("path/to/main.fxml"));
Parent mainRoot = mainLoader.load();
Scene mainScene = new Scene(mainRoot);
primaryStage.setScene(mainScene);
primaryStage.setTitle("My Application");
primaryStage.sizeToScene();
primaryStage.show();
}
private boolean checkLoginFromDisk() {
// ... etc
}
// for environments not supporting direct launch of JavaFX:
public static void main(String[] args) {
launch(args);
}
}
如果您不使用 FXML,您只需定义 classes 而不是 FXML 文件 + "login" 和 "main" 的控制器,但结构保持不变:
public class LoginView {
private final GridPane /* for example */ view ;
public LoginView() {
// setup UI, etc...
}
public Pane getView() {
return view ;
}
public boolean checkLogin() {
// etc...
}
}
和
public class MainView {
private BorderPane /* for example */ view ;
public MainView() {
// set up UI etc...
}
public Pane getView() {
return view ;
}
}
然后您的启动方法看起来像
@Override
public void start(Stage primaryStage) {
loggedIn = checkLoginFromDisk();
while (! loggedIn) {
LoginView loginView = new LoginView();
Scene loginScene = new Scene(loginView.getView());
primaryStage.setScene(loginScene);
primaryStage.setTitle("Login");
primaryStage.showAndWait();
loggedIn = loginView.checkLogin();
}
MainView mainView = new MainView();
Scene mainScene = new Scene(mainView.getView());
primaryStage.setScene(mainScene);
primaryStage.setTitle("My Application");
primaryStage.sizeToScene();
primaryStage.show();
}
显然,您可以根据需要以多种不同的方式重构它(重复使用相同的登录 class 或 fxml 实例,为主视图使用不同的阶段,等等)。
请注意,不需要使用传递给 start()
方法的阶段。所以如果你想要独立的 classes 来封装包含登录场景和主场景的舞台,你可以添加以下 classes:
public class LoginStage extends Stage {
private final LoginView loginView ;
public LoginStage() {
loginView = new LoginView();
setScene(new Scene(loginView.getView());
setTitle("Login");
}
public boolean checkLogin() {
return loginView.checkLogin();
}
}
并类似地制作 MainStage
class。 (在基于 FXML 的版本中,LoginStage
持有对 LoginController
的引用,并且只是在构造函数中加载 FXML 而不是实例化 LoginView
class。)然后
public class MyApplication extends Application {
private boolean loggedIn ;
@Override
public void start(Stage ignored) {
loggedIn = checkLoginFromDisk();
while (! loggedIn) {
LoginStage login = new LoginStage();
loginStage.showAndWait();
loggedIn = loginStage.checkLogin();
}
new MainStage().show();
}
// ...
}
这似乎与我要找的非常相似。它遵循jns的建议。 不理想但不可怕:
class LoginScene(stage: Stage) extends Scene(new VBox()) {
val vbox = this.getRoot.asInstanceOf[VBox]
...
}
class MainScene(stage: Stage) extends Scene(new VBox()) {
val vbox = this.getRoot.asInstanceOf[VBox]
...
}
class ApplicationStartup extends Application {
override def start(primaryStage: Stage): Unit = {
val scene = if (...) new LoginScene(primaryStage) else new MainScene(primaryStage)
primaryStage.setScene(scene)
primaryStage.show()
}
}
(代码以 Scala 呈现)
或者,从问题的评论中可以看出,可以忽略 primaryStage
并随意创建我们自己的,这正是我从一开始就想要的:
class MainDialog extends Application {
override def start(primaryStage: Stage): Unit = {
val newStage = new Stage {
setTitle("abcdef")
setScene(new Scene(new Button("Hello World")))
}
newStage.show()
}
}