Javafx 在同一场景中加载新的fxml

Javafx 在同一场景中加载新的fxml,javafx,fxml,scene,Javafx,Fxml,Scene,我有2个fxml文件: 布局标题、菜单栏和内容 Anchorpane应该放在另一个fxml文件的内容中 我想知道如何从主场景加载内容空间中的第二个文件。在javaFX中工作是一件好事,还是加载一个新场景更好 我正试图做类似的事情,但不起作用: @FXML private AnchorPane content; @FXML private void handleButtonAction(ActionEvent event) { content = (AnchorPane)

我有2个fxml文件:

布局标题、菜单栏和内容 Anchorpane应该放在另一个fxml文件的内容中 我想知道如何从主场景加载内容空间中的第二个文件。在javaFX中工作是一件好事,还是加载一个新场景更好

我正试图做类似的事情,但不起作用:

@FXML
private AnchorPane content;

@FXML
private void handleButtonAction(ActionEvent event) {        
    content = (AnchorPane) FXMLLoader.load("vista2.fxml");
}

谢谢您的帮助。

其他人可能有更好的解决方案,但我的解决方案是在外部fxml中使用一个简单的容器,如VBox,然后加载新内容并将其作为容器的子容器添加。如果您只加载一个或两个表单,这可能是一种方法。然而,对于一个更完整的框架,我发现这篇博文很有帮助:她为她的框架提供了源代码,其中包括奇特的转换。虽然它是用来管理顶级场景的,但我发现它也很容易适应管理内部内容区域。

为什么您的代码不起作用

加载程序将创建一个新的锚平面,但您永远不会将新窗格添加到场景图中的父窗格中

快速修复

而不是:

content = (AnchorPane) FXMLLoader.load("vista2.fxml");
写:

content.getChildren().setAll(FXMLLoader.load("vista2.fxml"));
用新vista替换内容子项。内容本身保留在场景图中,因此在设置其子对象时,同时也将它们附加到场景图中

您可能需要使用布局,例如,使用自动调整大小的布局,如堆叠窗格,而不是固定窗格,以获得您想要的准确行为

与其只是采用快速修复方法,我建议您查看下面链接的简单框架,因为这可能会为您提供一个更通用的机制,以获得您想要的行为

参考FXML导航框架

我创建了一个用于在主场景的一部分中交换fxml控制的内容窗格

该框架的机制与kithril的回答中建议的相同

外部fxml的主窗格充当子窗格的支架。 外部fxml的主控制器提供了一个公共方法,可用于交换子窗格。 方便的导航器类是使用外部布局的主控制器静态初始化的。 导航器提供了一个公共静态方法,通过调用主控制器上的方法将新的子窗格加载到主容器中。 子窗格由各自的fxml加载程序在导航器中生成。 为什么是框架

对于回答您的问题,该框架似乎有些过头了,也许是这样。然而,我发现与FXML相关的两个最常被问到的话题是:

FXML生成的窗格之间的导航解决了这个问题。 . 所以我觉得这个案例需要一个小的演示框架

框架输出示例

第一个屏幕显示显示第一个视景的应用程序布局。内容是在主应用程序布局中定义的标题和aliceblue颜色的可交换子内容窗格

在下一个屏幕中,用户已导航到第二个视景,该视景保留主布局中的恒定标题,并用新的珊瑚色子内容窗格替换原始子窗格。新的子项已从新的fxml文件加载

寻找更实质的东西

这是一个轻量级框架,它比这个问题中的示例框架更广泛、支持更好

想找更简单的吗

只需调出场景根:

其他选择


动画过渡和其他:

我不确定这有多有效,但似乎很好,而且比上面的方法简单得多

据我所知,这里发生的事情与应用程序类的Start方法中发生的事情非常相似:

private void buttonGoToWindow3Action(ActionEvent event) throws IOException{
    Parent window3; //we need to load the layout that we want to swap
    window3 = (StackPane)FXMLLoader.load(getClass().getResource("/ScenePackage/FXMLWindow3"));

    Scene newScene; //then we create a new scene with our new layout
    newScene = new Scene(window3);

    Stage mainWindow; //Here is the magic. We get the reference to main Stage.
    mainWindow = (Stage)  ((Node)event.getSource()).getScene().getWindow();

    mainWindow.setScene(newScene); //here we simply set the new scene
}
然而,我不是一个java专家,而且对编程非常陌生,所以如果有经验的人能够对它进行评估,那将是一件好事

编辑: 我找到了更简单的方法

转到MainApplication类并使静态阶段成为parentWindow

public static Stage parentWindow;
@Override
public void start(Stage stage) throws Exception {
    parentWindow = stage;

    Parent root = FXMLLoader.load(getClass().getResource("/ScenePackage/FXMLMainScene.fxml"));

    Scene scene = new Scene(root);

    stage.setScene(scene);
    stage.show();
}
现在,您可以进入主舞台,以便在程序中的任何位置执行类似操作来更改场景:

    Parent window1;
    window1 = FXMLLoader.load(getClass().getResource("/ScenePackage/FXMLWindow1.fxml"));

    //Scene newSceneWindow1 = new Scene(window1);

    Stage mainStage;
    //mainStage = (Stage)  ((Node)event.getSource()).getScene().getWindow();
    mainStage = MainApplication.parentWindow;
    mainStage.getScene().setRoot(newSceneWindow1); //we dont need to change whole sceene, only set new root.
我的例子是

使用:

Main.getNavigation().load(View2.URL_FXML).Show();
Main.getNavigation().GoBack();

在这种情况下,我建议您改用。首先为内容创建自定义组件:

class Content2 extends AnchorPane {
    Content() {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("vista2.fxml");
        loader.setRoot(this);
        loader.setController(this);
        loader.load();
    }
}
将vista2.fxml文件根目录中的AnchorPane标记替换为fx:root:

最后,在java代码或fxml中绑定自定义事件处理程序:

@FXML
onNextButtonClick() {
    Content2 content2 = new Content2();
    content2.setOnPreviousButtonClick((event) -> {
        Content1 content1 = new Content1();

        layout.getChildren().clear();
        layout.getChildren().add(content1);
    });

    layout.getChildren().clear();
    layout.getChildren().add(content2);    
}
如果您不想动态添加内容,只需将Visible设置为true或false即可 我尝试了大多数答案,但这不是我想要的,所以我只是用给出的理想来做这件事:

public class Main extends Application {
public static Stage homeStage;
@Override
public void start(Stage primaryStage) throws Exception{
    homeStage = primaryStage;
    Parent root = FXMLLoader.load(getClass().getResource("mainView.fxml"));
    root.getStylesheets().add(getClass().getResource("stylesheet/custom.css").toExternalForm());
    homeStage.setTitle("Classification of Living Organisms");
    homeStage.setScene(new Scene(root, 600, 500));
    homeStage.show();
}


public static void main(String[] args) {
    launch(args);
  }
}
这是我的主课。带有登录窗口/page mainView.fxml的Main.java。 在mainController.java类中使用了一点@Tomasz的想法,尽管在使用之前我有点困惑:

public void gotoSubMenu(Event event) {
    Parent window1;
    try {
        window1 = FXMLLoader.load(getClass().getResource("src/displayView.fxml"));
        Stage window1Stage;
        Scene window1Scene = new Scene(window1, 600, 500);
        window1Stage = Main.homeStage;
        window1Stage.setScene(window1Scene);
    } catch (IOException e) {
        e.printStackTrace();
    }
}
创建了一个新的父窗口 名为“window1”,它在src目录中加载了名为“displayView.fxml”的第二个fxml文件。 创建主视图阶段的对象,并将场景设置为根为window1的新创建场景。
希望这对现在进入JavaFX的人有所帮助。

如果您正在寻找一种方法使按钮调用新的fxml文件,这对我很有用

@FXML
private void mainBClicked(ActionEvent event) throws IOException {
    Stage stage;
    Parent root;
    stage=(Stage) ((Button)(event.getSource())).getScene().getWindow();
    root = FXMLLoader.load(getClass().getResource("MainMenu.fxml"));
    Scene scene = new Scene(root);
    stage.setScene(scene);
    stage.show();
}

保持相同的场景容器,但更改场景容器内的视图

假设要将新视图和控制器传递到的场景容器是名为sceneContainer的网格窗格布局

建立新视图的FXMLLoader对象

FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Notifications.fxml"));
创建一个匹配的容器并将新视图的内容加载到其中

GridPane yourNewView = fxmlLoader.load();
将新视图设置为sceneContainer。setAll首先清除所有子项

sceneContainer.getChildren().setAll(yourNewView);
获取新视图的控制器对象,并调用启动类逻辑的方法

Notifications notifications = fxmlLoader.getController();
notifications.passUserName(userName);
完整示例如下所示:

@FXML
public void setNotificationsViewToScene() {

    try {
         FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Notifications.fxml"));
         GridPane yourNewView = fxmlLoader.load();
         sceneContainer.getChildren().setAll(yourNewView);

         Notifications notifications = fxmlLoader.getController();
         notifications.passUserName(userName);

    } catch (IOException e) {
         e.printStackTrace();
    }
}

你的代码不起作用,因为加载程序创建了一个新的AnchorPane,但你从未将新窗格添加到场景图中的父级。请告诉我如何将新窗格添加到场景中的父级?我是使用JavaFXI的新手我创建了一个答案来提供有关fxml加载的更多细节。@jewelsea-非常感谢您提供这个小框架,用于将新fxml加载到同一场景。我已经在我的小应用程序中实现了这一点,但是如何在这个框架中引入模型、视图和控制器交互?您有任何指针或代码示例吗?非常感谢。你的答案看起来不错,但我无法解决这个问题。对于初学者来说,有关于这个问题的ant教程吗?@jewelsea,afterburner.fx已存档,它指向在线购物门户。您能否删除您的评论以避免垃圾邮件标志:@Raju谢谢您的评论,我删除了评论并添加了到afterburner.fx的链接。答案中的real site不是一个购物门户。@jewelsea有点离题,但什么被认为是更好的做法,在同一场景中加载新的fxml或切换不同的场景?您的回答很有帮助,但可以包括您链接到的视频的摘要或相关部分。这也将有助于你的答案保持良好,即使你包含的链接在未来中断。现在应该是清楚的。然而,我不能加载布局时,是不同于AnchorPane。应用程序抛出异常…将parentWindow公开并保持静态是一个非常糟糕的主意。请注意,原始解决方案没有假定窗口是由MainApplication类定义的,更不用说是它的主要阶段了;它根本没有引用那个类。因此,您的解决方案只适用于一种特定情况;使用原始版本更通用。@James\u您的意思是更好地执行someNode.getScene.getWindow?对耦合性较小。请注意,如果只是更改场景的根,则根本不需要窗口,只需执行someNode.getScene.setRoot。。。;
Notifications notifications = fxmlLoader.getController();
notifications.passUserName(userName);
@FXML
public void setNotificationsViewToScene() {

    try {
         FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Notifications.fxml"));
         GridPane yourNewView = fxmlLoader.load();
         sceneContainer.getChildren().setAll(yourNewView);

         Notifications notifications = fxmlLoader.getController();
         notifications.passUserName(userName);

    } catch (IOException e) {
         e.printStackTrace();
    }
}