Skip to content

Instantly share code, notes, and snippets.

@jewelsea
Last active March 13, 2024 22:49
Show Gist options
  • Save jewelsea/6460130 to your computer and use it in GitHub Desktop.
Save jewelsea/6460130 to your computer and use it in GitHub Desktop.
Small JavaFX framework for swapping in and out child panes in a main FXML container. Code is for Java 8+.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?scenebuilder-stylesheet vista.css?>
<VBox prefHeight="200.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml" fx:controller="MainController">
<children>
<Label fx:id="headerLabel" maxWidth="1.7976931348623157E308" text="Header" VBox.vgrow="NEVER" />
<StackPane fx:id="vistaHolder" VBox.vgrow="ALWAYS" />
</children>
</VBox>
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import java.io.IOException;
/**
* Main application class.
*/
public class Main extends Application {
@Override
public void start(Stage stage) throws Exception{
stage.setTitle("Vista Viewer");
stage.setScene(
createScene(
loadMainPane()
)
);
stage.show();
}
/**
* Loads the main fxml layout.
* Sets up the vista switching VistaNavigator.
* Loads the first vista into the fxml layout.
*
* @return the loaded pane.
* @throws IOException if the pane could not be loaded.
*/
private Pane loadMainPane() throws IOException {
FXMLLoader loader = new FXMLLoader();
Pane mainPane = (Pane) loader.load(
getClass().getResourceAsStream(
VistaNavigator.MAIN
)
);
MainController mainController = loader.getController();
VistaNavigator.setMainController(mainController);
VistaNavigator.loadVista(VistaNavigator.VISTA_1);
return mainPane;
}
/**
* Creates the main application scene.
*
* @param mainPane the main application layout.
*
* @return the created scene.
*/
private Scene createScene(Pane mainPane) {
Scene scene = new Scene(
mainPane
);
scene.getStylesheets().setAll(
getClass().getResource("vista.css").toExternalForm()
);
return scene;
}
public static void main(String[] args) {
launch(args);
}
}
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.layout.StackPane;
/**
* Main controller class for the entire layout.
*/
public class MainController {
/** Holder of a switchable vista. */
@FXML
private StackPane vistaHolder;
/**
* Replaces the vista displayed in the vista holder with a new vista.
*
* @param node the vista node to be swapped in.
*/
public void setVista(Node node) {
vistaHolder.getChildren().setAll(node);
}
}
/**
* vista.css
* Place in the same source directory as Main.java
* Ensure that your build system copies this file to your build output directory.
*/
#headerLabel {
-fx-background-color: steelblue;
-fx-text-fill: white;
-fx-padding: 5px;
}
#vistaHolder {
-fx-background-color: lightgrey;
}
#vista1 {
-fx-background-color: aliceblue;
}
#vista2 {
-fx-background-color: coral;
}
.button {
-fx-base: lightblue;
-fx-font-size: 20px;
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?scenebuilder-stylesheet vista.css?>
<StackPane fx:id="vista1" xmlns:fx="http://javafx.com/fxml" fx:controller="Vista1Controller">
<children>
<Button mnemonicParsing="false" onAction="#nextPane" text="Next Pane" />
</children>
</StackPane>
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
/**
* Controller class for the first vista.
*/
public class Vista1Controller {
/**
* Event handler fired when the user requests a new vista.
*
* @param event the event that triggered the handler.
*/
@FXML
void nextPane(ActionEvent event) {
VistaNavigator.loadVista(VistaNavigator.VISTA_2);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?scenebuilder-stylesheet vista.css?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.StackPane?>
<StackPane fx:id="vista2" xmlns:fx="http://javafx.com/fxml" fx:controller="Vista2Controller">
<children>
<Button mnemonicParsing="false" onAction="#previousPane" text="Previous Pane" />
</children>
</StackPane>
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
/**
* Controller class for the second vista.
*/
public class Vista2Controller {
/**
* Event handler fired when the user requests a previous vista.
*
* @param event the event that triggered the handler.
*/
@FXML
void previousPane(ActionEvent event) {
VistaNavigator.loadVista(VistaNavigator.VISTA_1);
}
}
import javafx.fxml.FXMLLoader;
import java.io.IOException;
/**
* Utility class for controlling navigation between vistas.
*
* All methods on the navigator are static to facilitate
* simple access from anywhere in the application.
*/
public class VistaNavigator {
/**
* Convenience constants for fxml layouts managed by the navigator.
*/
public static final String MAIN = "main.fxml";
public static final String VISTA_1 = "vista1.fxml";
public static final String VISTA_2 = "vista2.fxml";
/** The main application layout controller. */
private static MainController mainController;
/**
* Stores the main controller for later use in navigation tasks.
*
* @param mainController the main application layout controller.
*/
public static void setMainController(MainController mainController) {
VistaNavigator.mainController = mainController;
}
/**
* Loads the vista specified by the fxml file into the
* vistaHolder pane of the main application layout.
*
* Previously loaded vista for the same fxml file are not cached.
* The fxml is loaded anew and a new vista node hierarchy generated
* every time this method is invoked.
*
* A more sophisticated load function could potentially add some
* enhancements or optimizations, for example:
* cache FXMLLoaders
* cache loaded vista nodes, so they can be recalled or reused
* allow a user to specify vista node reuse or new creation
* allow back and forward history like a browser
*
* @param fxml the fxml file to be loaded.
*/
public static void loadVista(String fxml) {
try {
mainController.setVista(
FXMLLoader.load(
VistaNavigator.class.getResource(
fxml
)
)
);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@ivan047
Copy link

ivan047 commented Mar 4, 2017

@bigesta and those who have issues launching this code, you need to add the package name before the names of you FX controllers in .fxml files, so that:
fx:controller="sampleNameOfYourPackage.MainController">

Same to be done with vista1.fxml and vista2.fxml

@mmikhan
Copy link

mmikhan commented Mar 14, 2017

This worked like a charm for me. I was playing with this on a Maven directory structure project and had to change the path of the fxml and css files. This is one of the clean navigator.

Copy link

ghost commented Apr 10, 2017

Hello @jewelsea
I tried your code on StackOverflow but getting the error on load
------ content.getChildren().setAll(FXMLLoader.load("main.fxml"));

Please help. I am stuck at it whole day. Tried your above code also but instead of StackPane I used AnchorPane it is working but the controller is getting set on the header label when I press the next button where as in your case the header label is visible when you press next Button. I am designing an applcation which has 2 menubars on top as well as on side. Please help. Thank You in Advance

@BoGnY
Copy link

BoGnY commented Jan 19, 2018

I have built an example project with difference that there are packages and Gradle build system..
Then I have built also an example project with packages and Ant as default build system, and an example project with packages and Maven!!

All examples are based on your code 😃

@vigneshrcsengg
Copy link

vigneshrcsengg commented Apr 19, 2018

@jwelsea
Great framework, switching fxml page by using panes. I make it on work. Unfortunately i encountred an error with loading image, if i use your logic to load panes.

Image, stylesheet paths are not loaded as excepted like ../ui/header.jpg

The result in log i see 'null'

null/../ui/header.jpg

jfx-project-run:
Executing D:\DN-Workspace\DBS\dbs-dnstaging\dist\run1664401303\dbs-dnstaging.jar using platform C:\Program Files\Java\jdk1.8.0_162\jre/bin/java
null/../ui/header.jpg
Exception in Application start method
java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
	at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
	at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
	at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$154(LauncherImpl.java:182)
	at java.lang.Thread.run(Thread.java:748)
Caused by: javafx.fxml.LoadException: 
unknown path:24

	at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
	at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2579)
	at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2425)
	at com.dn.staging.main.DNStagingApplStart.loadMainPane(DNStagingApplStart.java:61)
	at com.dn.staging.main.DNStagingApplStart.start(DNStagingApplStart.java:45)
	at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$161(LauncherImpl.java:863)
	at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$174(PlatformImpl.java:326)
	at com.sun.javafx.application.PlatformImpl.lambda$null$172(PlatformImpl.java:295)
	at java.security.AccessController.doPrivileged(Native Method)
	at com.sun.javafx.application.PlatformImpl.lambda$runLater$173(PlatformImpl.java:294)
	at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
	at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
	at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
	... 1 more
Caused by: java.lang.IllegalArgumentException: Invalid URL: Invalid URL or resource not found
	at javafx.scene.image.Image.validateUrl(Image.java:1118)
	at javafx.scene.image.Image.<init>(Image.java:693)
	at com.sun.javafx.fxml.builder.JavaFXImageBuilder.build(JavaFXImageBuilder.java:47)
	at com.sun.javafx.fxml.builder.JavaFXImageBuilder.build(JavaFXImageBuilder.java:37)
	at javafx.fxml.FXMLLoader$ValueElement.processEndElement(FXMLLoader.java:763)
	at javafx.fxml.FXMLLoader.processEndElement(FXMLLoader.java:2823)
	at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2532)
	... 12 more
Caused by: java.lang.IllegalArgumentException: Invalid URL or resource not found
	at javafx.scene.image.Image.validateUrl(Image.java:1110)
	... 18 more
Exception running application com.dn.staging.main.DNStagingApplStart
Java Result: 1

If I load fxml files as usual way by parent class like below, my application can get load the images.

Parent root = FXMLLoader.load(getClass().getResource("/com/dn/staging/config/terminal/TerminalConfiguration.fxml"));
primaryStage.setTitle(appTitle());
primaryStage.setScene(new Scene(root));
//primaryStage.setFullScreen(true);
primaryStage.show();

What should be the probelm?? I'm new FX technology.

@KANE-99
Copy link

KANE-99 commented Sep 17, 2018

Thanks Man!!! You are Awesome 👍 :)

@cagatayyigit
Copy link

thanks for sharing. that is great

@MrBahur
Copy link

MrBahur commented May 17, 2020

thanks for sharing! awseome work.
I can send you a Maven project with packages and resources folder if you want.
(just needed to change a bit)

@engrmafzaalch
Copy link

Hey Thank you for this.... But i am facing an issue where i need to pass argument while loading a new fxml file .....i need to pass an argument while loading new fxml file kindly help me there

@xkkpafi
Copy link

xkkpafi commented Nov 14, 2021

Result: Exception in Application start method
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$154(LauncherImpl.java:182)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NullPointerException: inputStream is null.
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2455)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2425)
at replacepane.Main.loadMainPane(Main.java:50)
at replacepane.Main.start(Main.java:32)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$161(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$174(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null$172(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$173(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
... 1 more
Exception running application replacepane.Main

@Mameesh13
Copy link

awesome thanks man.

@Dmitry888888
Copy link

Everything is working! thanks a lot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment