如何在 JavaFX FXML 应用程序中打开额外的 window?
How to open an additional window in a JavaFX FXML app?
在我的 JavaFX FXML 应用程序中,我希望在用户单击主要 window 中某处的菜单项时弹出辅助 window,以便用户可以在其中输入一些内容,单击按钮后,它将被馈送到应用程序,辅助 window 将被关闭。
所有的教程都有些离谱。他们描述了如何在纯 JavaFX 中执行此操作,这显然与您使用 FXML 的方式不同,或者他们解释了如何切换场景,从而关闭旧场景。我想这会很简单,沿着定义 FXML 布局及其控制器的路线,用它们创建一个新场景,然后调用类似
的东西
theStage.showScene(userInputWindow);
但可行的解决方案似乎要复杂得多,其背后的原因与我的假设不同。例如在 this tutorial 中,我真的不明白他们为什么要把那个演员表放在那里,FXMLLoader() 实际会做什么,或者实际上我将如何调整其中的任何一个以适应手头的任务。此外,该资源声明 "the stage can only show 1 scene at a time"。在我看来,JavaFX 应用程序极不可能缺少在不关闭旧应用程序的情况下显示新 window 这样的微不足道的功能。也许我误解了什么是舞台和场景以及它们可以做什么。所以我需要知道:
如何用代码实现上面描述的效果?
解决方案背后的原因是什么?所有相关的东西在那里做什么?
在你的第一句话中,你描述了一个场景,看起来它是使用对话框的理想候选者。您看过对话框 class 了吗?当然,可以根据需要在 JavaFX 中打开任意数量的 windows(又名阶段),但对于您描述的场景,对话框似乎是更简单、更合适的解决方案。
您只能在 Stage
中展示一个场景,但您可以创建多个舞台。如果你想为你的辅助 window 使用 fxml,你应该掌握控制器实例并以允许你访问用户输入的方式设计控制器。您可以使用 Stage.showAndWait
到 "wait for the user to complete the input".
例子
应用启动方式
请注意,这里只是一个打开新 window 的按钮,但您可以在菜单项的 onAction
事件处理程序中使用类似的逻辑。 (在这种情况下,您需要使用 someNode.getScene().getWindow()
来访问 Stage.initOwner
的父 window;someNode
是父 [=37 中的任意 Node
=]; 您可以从事件中获取节点 (((Node)event.getTarget())
) 或使用场景中已知的节点;在 InputController.submit
中 mealField
用于此目的)
@Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Choose favorite meal");
Label label = new Label("I don't know your favorite meal yet!");
btn.setOnAction((ActionEvent event) -> {
FXMLLoader loader = new FXMLLoader(getClass().getResource("input.fxml"));
Scene newScene;
try {
newScene = new Scene(loader.load());
} catch (IOException ex) {
// TODO: handle error
return;
}
Stage inputStage = new Stage();
inputStage.initOwner(primaryStage);
inputStage.setScene(newScene);
inputStage.showAndWait();
String meal = loader.<InputController>getController().getMeal();
label.setText(meal == null ? "C'mon, tell me your favourite meal already!" : "Your favourite meal is "+meal+". Interesting!");
});
VBox root = new VBox(label, btn);
root.setSpacing(10);
root.setPadding(new Insets(10));
root.setPrefWidth(300);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
控制器
public class InputController {
@FXML
private TextField mealField;
private boolean mealChosen;
@FXML
private void submit() {
mealChosen = true;
mealField.getScene().getWindow().hide();
}
public String getMeal() {
return mealChosen ? mealField.getText() : null;
}
}
fxml
<GridPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="mypackage.InputController" vgap="10" hgap="10" >
<columnConstraints>
<ColumnConstraints prefWidth="150.0" />
<ColumnConstraints prefWidth="150.0" />
</columnConstraints>
<children>
<TextField GridPane.columnIndex="1" fx:id="mealField" onAction="#submit" />
<Button mnemonicParsing="false" text="Ok" GridPane.columnIndex="1" GridPane.rowIndex="1" onAction="#submit" />
<Label text="Your favourite meal" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</GridPane>
在我的 JavaFX FXML 应用程序中,我希望在用户单击主要 window 中某处的菜单项时弹出辅助 window,以便用户可以在其中输入一些内容,单击按钮后,它将被馈送到应用程序,辅助 window 将被关闭。
所有的教程都有些离谱。他们描述了如何在纯 JavaFX 中执行此操作,这显然与您使用 FXML 的方式不同,或者他们解释了如何切换场景,从而关闭旧场景。我想这会很简单,沿着定义 FXML 布局及其控制器的路线,用它们创建一个新场景,然后调用类似
的东西theStage.showScene(userInputWindow);
但可行的解决方案似乎要复杂得多,其背后的原因与我的假设不同。例如在 this tutorial 中,我真的不明白他们为什么要把那个演员表放在那里,FXMLLoader() 实际会做什么,或者实际上我将如何调整其中的任何一个以适应手头的任务。此外,该资源声明 "the stage can only show 1 scene at a time"。在我看来,JavaFX 应用程序极不可能缺少在不关闭旧应用程序的情况下显示新 window 这样的微不足道的功能。也许我误解了什么是舞台和场景以及它们可以做什么。所以我需要知道:
如何用代码实现上面描述的效果?
解决方案背后的原因是什么?所有相关的东西在那里做什么?
在你的第一句话中,你描述了一个场景,看起来它是使用对话框的理想候选者。您看过对话框 class 了吗?当然,可以根据需要在 JavaFX 中打开任意数量的 windows(又名阶段),但对于您描述的场景,对话框似乎是更简单、更合适的解决方案。
您只能在 Stage
中展示一个场景,但您可以创建多个舞台。如果你想为你的辅助 window 使用 fxml,你应该掌握控制器实例并以允许你访问用户输入的方式设计控制器。您可以使用 Stage.showAndWait
到 "wait for the user to complete the input".
例子
应用启动方式
请注意,这里只是一个打开新 window 的按钮,但您可以在菜单项的 onAction
事件处理程序中使用类似的逻辑。 (在这种情况下,您需要使用 someNode.getScene().getWindow()
来访问 Stage.initOwner
的父 window;someNode
是父 [=37 中的任意 Node
=]; 您可以从事件中获取节点 (((Node)event.getTarget())
) 或使用场景中已知的节点;在 InputController.submit
中 mealField
用于此目的)
@Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Choose favorite meal");
Label label = new Label("I don't know your favorite meal yet!");
btn.setOnAction((ActionEvent event) -> {
FXMLLoader loader = new FXMLLoader(getClass().getResource("input.fxml"));
Scene newScene;
try {
newScene = new Scene(loader.load());
} catch (IOException ex) {
// TODO: handle error
return;
}
Stage inputStage = new Stage();
inputStage.initOwner(primaryStage);
inputStage.setScene(newScene);
inputStage.showAndWait();
String meal = loader.<InputController>getController().getMeal();
label.setText(meal == null ? "C'mon, tell me your favourite meal already!" : "Your favourite meal is "+meal+". Interesting!");
});
VBox root = new VBox(label, btn);
root.setSpacing(10);
root.setPadding(new Insets(10));
root.setPrefWidth(300);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
控制器
public class InputController {
@FXML
private TextField mealField;
private boolean mealChosen;
@FXML
private void submit() {
mealChosen = true;
mealField.getScene().getWindow().hide();
}
public String getMeal() {
return mealChosen ? mealField.getText() : null;
}
}
fxml
<GridPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="mypackage.InputController" vgap="10" hgap="10" >
<columnConstraints>
<ColumnConstraints prefWidth="150.0" />
<ColumnConstraints prefWidth="150.0" />
</columnConstraints>
<children>
<TextField GridPane.columnIndex="1" fx:id="mealField" onAction="#submit" />
<Button mnemonicParsing="false" text="Ok" GridPane.columnIndex="1" GridPane.rowIndex="1" onAction="#submit" />
<Label text="Your favourite meal" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</GridPane>