43

我有 2 个 fxml 文件:

  • 布局(标题、菜单栏和内容)
  • Anchorpane(它应该放在另一个 fxml 文件的内容中)

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

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

@FXML
private AnchorPane content;

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

谢谢您的帮助。

4

8 回答 8

79

为什么您的代码不起作用

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

快速解决

代替:

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

写:

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

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

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

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

参考 FXML 导航框架

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

该框架的机制与基瑟里尔的回答中建议的相同。

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

为什么是框架

对于回答您的问题,该框架似乎有点矫枉过正,也许确实如此。但是,我发现与 FXML 相关的两个最常见的主题是:

  1. FXML 生成的窗格之间的导航(this question)。
  2. 如何在 FXML 控制器之间传递数据

所以我觉得这个案例需要一个小的演示框架。

示例框架输出

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

远景1

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

vista2

寻找更实质性的东西?

afterburner.fx是一个比这个问题的示例框架更广泛和更好支持的轻量级框架。

寻找更简单的东西?

只需换掉场景根目录:Changing Scenes in JavaFX

其他选择?

动画过渡和其他:在 JavaFX 中的窗格之间切换

于 2013-09-06T06:52:26.290 回答
6

我不确定这有多有效,但似乎很好,而且比上述方法更简单。

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.
于 2015-01-26T20:27:38.183 回答
0

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

于 2013-09-05T03:29:39.550 回答
0

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

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

将文件AnchorPane根目录中的标记替换为:vista2.fxmlfx: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

于 2016-10-16T06:22:44.190 回答
0

我的面具的例子。

使用:

Main.getNavigation().load(View2.URL_FXML).Show();
Main.getNavigation().GoBack();
于 2015-09-17T17:14:07.313 回答
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();
    }
}
于 2020-01-25T15:11:40.230 回答
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);
  }
}

这是我的主要课程。Main.java 与登陆窗口/页面 mainView.fxml。使用了一点@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();
    }
}

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

于 2017-02-07T19:14:40.323 回答
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();
}
于 2018-02-25T06:24:37.237 回答