在同一场景中加载新的fxml

时间:2013-09-04 16:39:46

标签: javafx fxml scene

我有2个fxml文件:

  • 布局(标题,菜单栏和内容)
  • Anchorpane(它应该放在其他fxml文件的内容中)

我想知道如何在“Master”场景中加载内容空间中的第二个文件。在javaFX中工作是一件好事还是加载新场景更好?

我正在尝试做这样的事情,但它不起作用:

@FXML
private AnchorPane content;

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

感谢您的帮助。

8 个答案:

答案 0 :(得分:64)

为什么您的代码不起作用

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

快速修复

而不是:

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

写:

content.getChildren().setAll(FXMLLoader.load("vista2.fxml"));

用新视图替换内容子项。内容本身保留在场景图中,因此当您设置它的子项时,您也会同时将它们附加到场景图中。

您可能需要使用布局(例如,使用自动调整大小布局(如StackPanes而不是AnchorPanes)来获得所需的确切行为。

我建议不要仅采用快速修复,而是检查下面链接的简单框架,因为它可能会为您提供更通用的机制来获取您想要的行为。

参考FXML导航框架

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

框架的机制与kithril的回答中提出的相同。

  1. 外部fxml的主窗格充当子窗格的持有者。
  2. 外部fxml的主控制器提供可用于交换子窗格的公共方法。
  3. 使用外部布局的主控制器静态初始化便捷导航器类。
  4. 导航器提供了一个公共静态方法,用于将新的子窗格加载到主容器中(通过在主控制器上调用方法)。
  5. 子窗格由导航器中各自的fxml加载器生成。
  6. 为何选择框架

    这个框架对于回答你的问题似乎有些过分,也许就是这样。但是,我发现与FXML有关的两个最常见的主题是:

    1. FXML生成的窗格之间的导航(此问题)。
    2. How to pass data between FXML controllers
    3. 所以我觉得这个案例需要一个小的演示框架。

      示例框架输出

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

      vista1

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

      vista2

      寻找更重要的东西?

      这个问题的示例框架比afterburner.fx更广泛,更受支持的轻量级框架。

      寻找更简单的东西?

      只需换出场景根:Changing Scenes in JavaFX

      其他选项?

      动画过渡和其他:Switch between panes in JavaFX

答案 1 :(得分:5)

我不确定这是多么有效,但似乎很好,而且对于上述方法来说更简单,更简单。

https://www.youtube.com/watch?v=LDVztNtJWOo

据我所知,这里发生了什么(它与应用程序类中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类并创建静态Stage 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.

答案 2 :(得分:0)

其他人可能有更好的解决方案,但我的解决方案是在外部fxml中有一个像VBox这样的简单容器,然后加载新内容并将其添加为容器的子容器。如果您只加载一个或两个表单,这可能是最佳选择。但是,对于一个更完整的框架,我发现这篇博文有用:https://blogs.oracle.com/acaicedo/entry/managing_multiple_screens_in_javafx1她有她的框架的源代码,其中包括花哨的过渡。虽然它旨在管理顶级场景,但我发现它也很容易适应管理内部内容区域。

答案 3 :(得分:0)

mask的例子。

使用:

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

答案 4 :(得分:0)

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

class Content2 extends AnchorPane {
    Content() {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("vista2.fxml");
        loader.setRoot(this);
        loader.setController(this);
        loader.load();
    }
}

使用AnchorPane替换vista2.fxml文件根目录中的fx:root标记:

<fx:root type="javafx.scene.layout.AnchorPane" xmlns:fx="http://javafx.com/fxml">
    ...
</fx:root>

然后,您只需使用自定义事件绑定和箭头功能即可完成此操作。将事件处理程序属性添加到Content类:

private final ObjectProperty<EventHandler<ActionEvent>> propertyOnPreviousButtonClick = new SimpleObjectProperty<EventHandler<ActionEvent>>();

@FXML
private void onPreviousButtonClick(ActionEvent event) {
    propertyOnPreviousButtonClick.get().handle(event)
}

public void setOnPreviousButtonClick(EventHandler<ActionEvent> handler) {
    propertyOnPreviousButtonClick.set(handler);
}

最后,在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);    
}

如果您不想动态添加内容,只需setVisible()truefalse

答案 5 :(得分:0)

也陷入困境 试过大部分答案,不是我想要的,所以我只是用了理想来做到这一点:

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);
  }
}
这是我的主要课程。带着陆窗口/页面mainView.fxml的Main.java。 使用了一些@Tomasz的想法,虽然在我在mainController.java类中执行此操作之前让我感到困惑:

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();
    }
}

创建了一个名为&#39; window1&#39;的新父窗口。加载了名为&#39; displayView.fxml&#39;的第二个fxml文件。在src目录中。 创建了主视图阶段的对象,并将场景设置为新创建的场景,其根目录为window1。 希望这有助于现在进入#JavaFX的人。

答案 6 :(得分:0)

如果您正在寻找一种方法让按钮调用新的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();
}

答案 7 :(得分:0)

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

说要传递新视图和控制器到的场景容器是一个名为sceneContainer的GridPane布局。

建立新视图的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();
    }
}