我有一个具有子窗口或阶段的JavaFX应用程序。我处理所有阶段如何相互交互的方式是这样的:我在我的Main类中有一个名为GuiContainer的静态类集合。 GuiContainer看起来像这样:
public class GuiContainer {
private final Stage stage;
private final Scene scene;
private final FXMLLoader loader;
private final Controller controller;
private final Parent parent;
public GuiContainer(Stage stage, Scene scene, FXMLLoader loader, Controller controller, Parent parent) {
this.stage = stage;
this.scene = scene;
this.loader = loader;
this.controller = controller;
this.parent = parent;
}
public Stage getStage() {
return stage;
}
public Scene getScene() {
return scene;
}
public FXMLLoader getLoader() {
return loader;
}
public Controller getController() {
return controller;
}
public Parent getParent() {
return parent;
}
public static final GuiContainer createMain(Stage stage, String title, int width, int height, String pathToView) throws Exception {
FXMLLoader loaderInner = new FXMLLoader(GuiContainer.class.getResource(pathToView));
Parent parentInner = loaderInner.load();
final Controller controllerInner = loaderInner.getController();
Scene sceneInner = new Scene(parentInner, width, height);
stage.setTitle(title);
stage.setScene(sceneInner);
GuiContainer guiContainer = new GuiContainer(
stage, sceneInner, loaderInner, controllerInner, parentInner
);
controllerInner.start(guiContainer);
return guiContainer;
}
public static final GuiContainer createModal(Window owner, String title, int width, int height, String pathToView) throws Exception {
if (owner == null) {
Log.error(GuiContainer.class.getSimpleName(), "Unable to create instance, missing window owner!");
return null;
}
Stage stageInner = new Stage();
FXMLLoader loaderInner = new FXMLLoader(GuiContainer.class.getResource(pathToView));
Parent parentInner = loaderInner.load();
Controller controllerInner = loaderInner.getController();
Scene sceneInner = new Scene(parentInner, width, height);
stageInner.setTitle(title);
stageInner.setScene(sceneInner);
stageInner.initStyle(StageStyle.DECORATED);
stageInner.initModality(Modality.WINDOW_MODAL);
stageInner.initOwner(owner);
GuiContainer guiContainer = new GuiContainer(
stageInner, sceneInner, loaderInner, controllerInner, parentInner
);
controllerInner.start(guiContainer);
return guiContainer;
}
}
这样我就可以从任何地方访问任何舞台或控制器。
所有可能的GuiContainers仅在启动时创建一次(在静态main(String [] args)中),然后可以从任何地方静态访问。
现在......在主应用程序中(主要阶段内的场景)我有一个具有自定义单元工厂的TreeView,当右键单击一个单元格时,会显示相应的上下文菜单。在这个上下文菜单中,我打开一个子/模态阶段,如下所示:
String containerName = "guiName";
GuiContainer container = Main.getGuiContainers().getOrDefault(containerName, null);
if(container == null) {
Log.error(getClass().getSimpleName(), "Unable to find GuiContainer: " + containerName);
return;
}
container.getStage().showAndWait();
现在,问题来了。 JavaFX不会请求关注子阶段。例如,我无法键入TextField(在子舞台上),因为我在主舞台上注册了一个onKeyPressed事件,它捕获了我按下的所有键。在这个子阶段我也有一个ComboBox,当我从该ComboBox中选择一个项目时,子阶段终于成为焦点,主阶段不再捕获我按下的键。
我也尝试将模态更改为所有可能的值,并阅读他们在oracle.com上所做的事情,但没有任何信息对我有帮助......
我的代码有问题吗?或者这可能是JavaFX的问题?有什么想法吗?
编辑:这是我的应用程序覆盖启动方法:(我希望这提供更多信息)
@Override
public void start(Stage stage) throws Exception {
GuiContainer guiWindow1 = GuiContainer.createMain(stage, "Window1", 900, 460,
"/com/project/app/gui/views/Window1.fxml");
GuiContainer guiWindow2 = GuiContainer.createModal(stage, "Window2", 320, 240,
"/com/project/app/gui/views/Window2.fxml");
GuiContainer guiWindow3 = GuiContainer.createModal(stage, "Window3", 320, 240,
"/com/project/app/gui/views/Window3.fxml");
GuiContainer guiWindow4 = GuiContainer.createModal(stage, "Window4", 420, 360,
"/com/project/app/gui/views/Window4.fxml");
GuiContainer guiWindow5 = GuiContainer.createModal(stage, "Window5", 380, 460,
"/com/project/app/gui/views/Window5.fxml");
guiWindow5.getStage().setResizable(false);
guiContainers.put("guiWindow1", guiWindow1);
guiContainers.put("guiWindow2", guiWindow2);
guiContainers.put("guiWindow3", guiWindow3);
guiContainers.put("guiWindow4", guiWindow4);
guiContainers.put("guiWindow5", guiWindow5);
guiWindow1.getStage().show();
}
EDIT2:这是我注册onKeyPressed侦听器的方式:(在我的父控制器init()方法中)
getGuiContainer().getScene().setOnKeyPressed((e) -> {
Log.info(getClass().getSimpleName(), "Key pressed: " + e.getCode().getName());
Macro macro = Main.getProject().getSelectedMacro();
if(macro == null) {
Log.error(getClass().getSimpleName(), "Unable to find selected macro");
return;
}
if (e.getCode() == KeyCode.ESCAPE) {
macro.getNodePlacement().reset();
macro.cancelSelect();
macro.repaint();
} else if(e.getCode() == KeyCode.DELETE) {
macro.deleteSelectedNodes();
}
});
EDIT3:这就是我的抽象控制器类的样子 - 我希望这能提供更好的洞察力
public abstract class Controller {
private GuiContainer guiContainer;
public final GuiContainer getGuiContainer() {
return guiContainer;
}
public final void start(GuiContainer guiContainer) {
this.guiContainer = guiContainer;
init();
}
public abstract void init(); // this is where the onKeyPressed is registered, when extending the abstract class
public abstract void reset();
public abstract void refresh();
}
重要编辑:这只发生在MAC平台上,我在Windows上运行相同的应用程序,没有这样的问题。
答案 0 :(得分:1)
请你发布舞台构造函数的代码吗?
请注意doc:
请注意,显示模态阶段不一定会阻止调用者。 无论方式如何,show()方法都会立即返回 舞台。如果需要阻止,请使用showAndWait()方法 调用者直到模态阶段被隐藏(关闭)。模式必须是 在>之前初始化使舞台可见。
编辑 - 我的错误,我没有注意到createModal()方法。 我在一些自定义阶段最终做的是:
Platform.runLater(() -> somecontrol.requestFocus());
(java 8,显然,如果lambda不适用于您当前的语言级别(< 8),则覆盖run()方法。
但我只在非模态阶段这样做。模态阶段应该自动关注焦点。
根据Modality的文档,APPLICATION_MODAL应该按预期执行(包括阻止其他窗口的事件):
APPLICATION_MODAL定义阻止事件发生的模态窗口 发送到任何其他应用程序窗口。
在调用initOwner之前,使用WINDOW_MODAL调用initModality。请参阅:
WINDOW_MODAL public static final模态WINDOW_MODAL定义模态 阻止事件传递给其整个所有者的窗口 窗口层次结构注意:模式设置为WINDOW_MODAL的舞台,但是 它的所有者为null,被视为其模态设置为NONE。
如果requestFocus()(显示模式阶段之后 >>, UI线程对吗?)没有关注焦点,我想你的主要窗口会自动将其取回。