在同一场景中加载新的fxml

我有2个fxml文件:

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

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

我试图做这样的事情,但它不工作:

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

谢谢您的帮助。

为什么你的代码不工作

加载器会创build一个新的AnchorPane,但是您绝对不会将新窗格添加到场景图中的父级。

快速解决

代替:

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

写:

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

用新的vistareplace内容的孩子。 内容本身保留在场景图中,所以当你设置它的孩子的时候,你也将它们同时附加到场景图中。

您可能需要使用布局(例如使用像StackPanes而不是AnchorPanes这样的自动resize的布局)来获得所需的确切行为。

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

参考FXML导航框架

我创build了一个小框架来交换fxml控制的内容窗格进出一部分主场景。

这个框架的机制和kithril的答案中提出的一样。

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

为什么一个框架

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

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

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

示例框架输出

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

vista1

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

vista2

寻找更重要的东西?

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

寻找更简单的东西?

换掉场景根目录: 改变JavaFX中的场景 。

其他选项?

animation转换和其他: 在JavaFX中的窗格之间切换

我不知道这是多么有效,但似乎是很好,更重要的是,上述方法更简单。

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类并创build静态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. 

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

我的面具的例子。

使用:

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

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

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

AnchorPane fx:rootreplacevista2.fxml文件根目录中的AnchorPane标记:

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

然后,只需使用自定义事件绑定和箭头function即可完成此操作。 将事件处理程序属性添加到您的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); } 

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

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

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

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