I am learning JavaFX with: https://code.makery.ch/library/javafx-tutorial/part1/
package ch.makery.address.controller;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class MainApp extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("AddressApp");
initRootLayout();
showPersonOverview();
}
/**
* Initializes the root layout.
*/
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("ch.makery.address.view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Shows the person overview inside the root layout.
*/
public void showPersonOverview() {
try {
// Load person overview.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("ch.makery.address.view/PersonOverview.fxml"));
AnchorPane personOverview = (AnchorPane) loader.load();
// Set person overview into the center of root layout.
rootLayout.setCenter(personOverview);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Returns the main stage.
* #return
*/
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
When I'm trying to load fxml according to author:
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("ch.makery.address.view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
I get such exception:
Exception in Application start method
java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:464)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
Caused by: java.lang.RuntimeException: Exception in Application start method
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:900)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.IllegalStateException: Location is not set.
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2459)
at javafx.fxml/javafx.fxml.FXMLLoader.load(FXMLLoader.java:2435)
at ch.makery.address.controller.MainApp.initRootLayout(MainApp.java:40)
at ch.makery.address.controller.MainApp.start(MainApp.java:27)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:846)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:455)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(GtkApplication.java:277)
... 1 more
Exception running application ch.makery.address.controller.MainApp
I tried to write path different ways but nothing. So, can anyone help me understand how to handle with paths and loader?
I am working on Netbeans 11 and my project layout:
If I try to load a screen with a button called "vorige" it go's in an error.
This is the error I got :
I find it strange because the logout button works in exactly the same way and there is no error there. If I remove the onAction method in the FXML file the exception is gone.
Kind regards
Fietsschermcontroller
#FXML
private Button buVorige;
/**
* Initializes the controller class.
*/
public void setMain(Vivesbike main) {
this.main = main;
}
/**
*
* #param vorigeEvent
*/
public void Vorige(ActionEvent vorigeEvent){
main.loadBeginscherm();
}
Fietsscherm FXML
<Button fx:id="buVorige" layoutX="31.0" layoutY="23.0" mnemonicParsing="false" onAction="#Vorige" prefHeight="39.0" prefWidth="102.0" text="Vorige" />
This function is in the main
public void loadBeginscherm(){
try {
FXMLLoader loader = new FXMLLoader(this.getClass().getResource("view/Beginscherm.fxml"));
BeginschermController controller = new BeginschermController();
loader.setController(controller);
Parent root = loader.load();
controller.setMain(this);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.centerOnScreen();
stage.setTitle("Keuzescherm");
stage.show();
} catch (IOException ex) {
System.out.println("Fout tijdens het laden van het beginscherm: "+ex.getMessage());
}
}
public void loadFietsscherm(){
try {
FXMLLoader loader = new FXMLLoader(this.getClass().getResource("view/Fietsscherm.fxml"));
BeginschermController controller = new BeginschermController();
loader.setController(controller);
Parent root = loader.load();
controller.setMain(this);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.centerOnScreen();
stage.setTitle("Fietsscherm");
stage.show();
} catch (IOException ex) {
System.out.println("Fout tijdens het laden van het fietsscherm: "+ex.getMessage());
}
}
In the loadFietsscherm method you connect BeginschermController to Fietsscherm.fxml, but I'm sure FietsschermController is implied to be there.
I am creating a ListView with the ability to double click each item and have a window popup with inputs structured by an FXML file. The FXML file itemStep contains fx:controller="controller.ItemStep"
listViewVariable.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (mouseEvent.getButton().equals(MouseButton.PRIMARY)) {
if (mouseEvent.getClickCount() == 2) {
ItemStep item = listViewVariable.getSelectionModel()
.getSelectedItem();
if (item != null) {
try {
FXMLLoader isLoader = new FXMLLoader(Main.class.getResource("/view/itemStep.fxml"));
AnchorPane pane = isLoader.load();
Scene scene = new Scene(pane);
Stage stage = new Stage();
stage.setScene(scene);
item.setUrl(item.urlField.getText());
stage.show();
stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
public void handle(WindowEvent we) {
item.setUrl(item.urlField.getText());
}
});
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
});
I continue to get the following error using the above. I need to be able use the FXML file within this Stage.
Caused by: java.lang.InstantiationException: controller.ItemStep
at java.lang.Class.newInstance(Unknown Source)
at sun.reflect.misc.ReflectUtil.newInstance(Unknown Source)
... 44 more
Caused by: java.lang.NoSuchMethodException: controller.ItemStep.<init>()
at java.lang.Class.getConstructor0(Unknown Source)
... 46 more
Your ItemStep class does not have a no argument constructor.
Does a new instance of ItemStep get recreated after each double[click]?
Yes, that is what you have wrote your code to do.
If you didn't wish to do that, you should invoke the load method on your FXMLLoader outside of your event handler, store the reference to the loaded pane in a final variable, and use that reference inside the event handler, rather than loading a new pane time every time the event handler is invoked.
when I invoke both the load FXMLLoader and final AnchorPane outside the event handler I get: AnchorPane#ed39805[styleClass=root]is already set as root of another scene
You can't add a node to more than one scene or to a single scene more than once, you either have to remove it from the original scene, or just re-use the original scene with a single instance of the node. I don't know what kind of behavior you desire, but probably, you want to just have a single window pop-up and make it modal, rather than creating a new window every time somebody clicks on something.
Basically, you do something like this (though this is just an outline that I have never compiled or executed as I don't completely know your complete requirements, nor what your ItemStep and urlField really are):
final FXMLLoader isLoader = new FXMLLoader(Main.class.getResource("/view/itemStep.fxml"));
final AnchorPane pane = isLoader.load();
final Scene scene = new Scene(pane);
final Stage stage = new Stage();
stage.initModality(Modality.APPLICATION_MODAL);
stage.setScene(scene);
listViewVariable.setOnMouseClicked(event -> {
if (mouseEvent.getButton().equals(MouseButton.PRIMARY) && (mouseEvent.getClickCount() == 2)) {
ItemStep item = listViewVariable.getSelectionModel().getSelectedItem();
if (item != null) {
stage.showAndWait();
item.setUrl(item.urlField.getText());
}
}
});
I have a JavaFX application that has child windows or stages. The way I handle how all stages interact with each other is like this: I have a static collection of classes called GuiContainer in my Main class. The GuiContainer looks like this:
public class GuiContainer {
private final Stage stage;
private final Scene scene;
private final FXMLLoader loader;
private final Controller controller;
private final Parent parent;
public GuiContainer(Stage stage, Scene scene, FXMLLoader loader, Controller controller, Parent parent) {
this.stage = stage;
this.scene = scene;
this.loader = loader;
this.controller = controller;
this.parent = parent;
}
public Stage getStage() {
return stage;
}
public Scene getScene() {
return scene;
}
public FXMLLoader getLoader() {
return loader;
}
public Controller getController() {
return controller;
}
public Parent getParent() {
return parent;
}
public static final GuiContainer createMain(Stage stage, String title, int width, int height, String pathToView) throws Exception {
FXMLLoader loaderInner = new FXMLLoader(GuiContainer.class.getResource(pathToView));
Parent parentInner = loaderInner.load();
final Controller controllerInner = loaderInner.getController();
Scene sceneInner = new Scene(parentInner, width, height);
stage.setTitle(title);
stage.setScene(sceneInner);
GuiContainer guiContainer = new GuiContainer(
stage, sceneInner, loaderInner, controllerInner, parentInner
);
controllerInner.start(guiContainer);
return guiContainer;
}
public static final GuiContainer createModal(Window owner, String title, int width, int height, String pathToView) throws Exception {
if (owner == null) {
Log.error(GuiContainer.class.getSimpleName(), "Unable to create instance, missing window owner!");
return null;
}
Stage stageInner = new Stage();
FXMLLoader loaderInner = new FXMLLoader(GuiContainer.class.getResource(pathToView));
Parent parentInner = loaderInner.load();
Controller controllerInner = loaderInner.getController();
Scene sceneInner = new Scene(parentInner, width, height);
stageInner.setTitle(title);
stageInner.setScene(sceneInner);
stageInner.initStyle(StageStyle.DECORATED);
stageInner.initModality(Modality.WINDOW_MODAL);
stageInner.initOwner(owner);
GuiContainer guiContainer = new GuiContainer(
stageInner, sceneInner, loaderInner, controllerInner, parentInner
);
controllerInner.start(guiContainer);
return guiContainer;
}
}
This way I have access to any stage or controller from anywhere.
All possible GuiContainers are created only once at boot (in static main(String[] args)) and can then be statically accessed from anywhere, by anyone.
Now... in the main application (scene inside the primary stage) I have a TreeView that has a custom cell factory, and when a cell is right clicked an appropriate context menu is shown. Inside this context menu I open a child/modal stage, like so:
String containerName = "guiName";
GuiContainer container = Main.getGuiContainers().getOrDefault(containerName, null);
if(container == null) {
Log.error(getClass().getSimpleName(), "Unable to find GuiContainer: " + containerName);
return;
}
container.getStage().showAndWait();
Now, here comes the problem. JavaFX doesn't request focus on the child stage. For instance, I can't type into a TextField (on the child stage) because I have an onKeyPressed event registered on the primary stage and it captures all the keys I press. On this child stage I also have a ComboBox, and when I select an item from that ComboBox, the child stage finally comes into focus and the primary stage no longer captures the keys I press.
I also tried to change the modality to all posible values and read what they do on oracle.com, but none of the information helped me...
Is there something wrong with my code? Or could this be an issue with JavaFX? Any ideas?
EDIT: Here is my Application overriden start method: (I hope this provides more information)
#Override
public void start(Stage stage) throws Exception {
GuiContainer guiWindow1 = GuiContainer.createMain(stage, "Window1", 900, 460,
"/com/project/app/gui/views/Window1.fxml");
GuiContainer guiWindow2 = GuiContainer.createModal(stage, "Window2", 320, 240,
"/com/project/app/gui/views/Window2.fxml");
GuiContainer guiWindow3 = GuiContainer.createModal(stage, "Window3", 320, 240,
"/com/project/app/gui/views/Window3.fxml");
GuiContainer guiWindow4 = GuiContainer.createModal(stage, "Window4", 420, 360,
"/com/project/app/gui/views/Window4.fxml");
GuiContainer guiWindow5 = GuiContainer.createModal(stage, "Window5", 380, 460,
"/com/project/app/gui/views/Window5.fxml");
guiWindow5.getStage().setResizable(false);
guiContainers.put("guiWindow1", guiWindow1);
guiContainers.put("guiWindow2", guiWindow2);
guiContainers.put("guiWindow3", guiWindow3);
guiContainers.put("guiWindow4", guiWindow4);
guiContainers.put("guiWindow5", guiWindow5);
guiWindow1.getStage().show();
}
EDIT2: This is how I register the onKeyPressed listener: (in my parent controller init() method)
getGuiContainer().getScene().setOnKeyPressed((e) -> {
Log.info(getClass().getSimpleName(), "Key pressed: " + e.getCode().getName());
Macro macro = Main.getProject().getSelectedMacro();
if(macro == null) {
Log.error(getClass().getSimpleName(), "Unable to find selected macro");
return;
}
if (e.getCode() == KeyCode.ESCAPE) {
macro.getNodePlacement().reset();
macro.cancelSelect();
macro.repaint();
} else if(e.getCode() == KeyCode.DELETE) {
macro.deleteSelectedNodes();
}
});
EDIT3: This is how my abstract controller class looks like - I hope this provides a better insight
public abstract class Controller {
private GuiContainer guiContainer;
public final GuiContainer getGuiContainer() {
return guiContainer;
}
public final void start(GuiContainer guiContainer) {
this.guiContainer = guiContainer;
init();
}
public abstract void init(); // this is where the onKeyPressed is registered, when extending the abstract class
public abstract void reset();
public abstract void refresh();
}
IMPORTANT EDIT: This only happens on the MAC platform, I ran the same application on windows and there were no such problems.
could you please post the code of the constructor of your stage?
Just a note, from the doc:
Note that showing a modal stage does not necessarily block the caller.
The show() method returns immediately regardless of the modality of
the stage. Use the showAndWait() method if you need to block the
caller until the modal stage is hidden (closed). The modality must be
initialized before the stage is made visible.
EDIT - My mistake, I didn't notice the createModal() method.
What I ended up doing in some of my custom stages is:
Platform.runLater(() -> somecontrol.requestFocus());
(java 8, obviously, override run() method if lambda is not available with your current language level (< 8)).
But I only do this for non-modal stage. Modal stage should take the focus automatically.
According to the doc of Modality, APPLICATION_MODAL is supposed to do what you expect (including Blocking the events for other windows):
APPLICATION_MODAL Defines a modal window that blocks events from being
delivered to any other application window.
You call initModality with WINDOW_MODAL before calling initOwner. Please see:
WINDOW_MODAL public static final Modality WINDOW_MODAL Defines a modal
window that block events from being delivered to its entire owner
window hierarchy. Note: A Stage with modality set to WINDOW_MODAL, but
its owner is null, is treated as if its modality is set to NONE.
If requestFocus() (called after the modal stage has been displayed, and in the UI thread right?) doesn't take the focus, I guess your main window is automatically taking it back.
I try to switch between scenes in same stage. I'm beginner in JavaFX so I don't know how to do it easily without spaghetti code. When I start code below I get null pointer at rootLayout.setCenter(content) in showCarChoosePage method (second scene). I know rootLayout is null and I was trying create new scene with and load it to primaryStage but then I got null pointer too. showCarChoosePage method is calling from LoginController. Thanks for your help
public class MC extends Application {
public Scene scene;
private GridPane grid;
public AnchorPane content;
public BorderPane rootLayout;
public Stage primaryStage;
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("VMHT v0.1");
try {
FXMLLoader loader = new FXMLLoader(MC.class.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
showLoginPage();
//showCarChoosePage();
}
public void showLoginPage(){
try {
FXMLLoader loader = new FXMLLoader(MC.class.getResource("view/LoginView.fxml"));
content = (AnchorPane) loader.load();
rootLayout.setCenter(content);
LoginController controller = loader.getController();
controller.setMC(this);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void showCarChoosePage(){
try {
FXMLLoader loader = new FXMLLoader(MC.class.getResource("view/CarChooseView.fxml"));
AnchorPane content = (AnchorPane) loader.load();
rootLayout.setCenter(content);
CarChooseController controller = loader.getController();
controller.setMC(this);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Here is another way to handle multiple scenes. It uses Controllers for each scene. You must pass the initial stage that was created in Main to each new Controller. You must also pass whatever data you want to share from Controller to Controller. This is all done with methods that are added to your controllers.
In this simple case a Person object was created with name, sex, and age class variables as well as setters and getters for these objects.
Then I created 3 fxml files (using SceneBuilder) that displayed the name, sex, and age values. Of course, I also created 3 controllers, one for each fxml file.
The user was allowed to edit these values. One scene allowed name entry, another allowed sex entry, and the last allowed age entry. This simulated a complex app where data entry and processing was divided among 3 different scenes.
The three fxml files and the 3 controllers look very similar to one another. Here is the NameController. It first has setters for the Stage and Person objects. It also has button event handlers to allow the user to navigate to the other stages.
package multiscene;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import java.net.URL;
import java.util.ResourceBundle;
public class NameController implements Initializable {
#FXML private TextField lblSex;
#FXML private TextField lblAge;
#FXML private TextField txtName;
private Stage myStage;
private Person person;
public void setStage(Stage myStage) {
this.myStage = myStage;
}
public void setPerson(Person person) {
this.person = person;
lblAge.setText(person.getAge());
txtName.setText(person.getName());
lblSex.setText(person.getSex());
}
#FXML
private void ageClicked(ActionEvent event) throws Exception{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(NameController.class.getResource("Age.fxml"));
AnchorPane page = (AnchorPane) loader.load();
Stage dialogStage = new Stage();
dialogStage.setTitle("Person Editor");
// dialogStage.initModality(Modality.WINDOW_MODAL);
//dialogStage.initOwner(primaryStage);
Scene scene = new Scene(page);
dialogStage.setScene(scene);
// Set the person into the controller
person.setName(txtName.getText());
AgeController newController = loader.getController();
newController.setStage(dialogStage);
newController.setPerson(person);
dialogStage.show();
myStage.close();
}
#FXML
private void sexClicked(ActionEvent event) throws Exception {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(AgeController.class.getResource("Sex.fxml"));
AnchorPane page = (AnchorPane) loader.load();
Stage dialogStage = new Stage();
dialogStage.setTitle("Person Editor");
// dialogStage.initModality(Modality.WINDOW_MODAL);
//dialogStage.initOwner(primaryStage);
Scene scene = new Scene(page);
dialogStage.setScene(scene);
// Set the person into the controller
person.setName(txtName.getText());
SexController newController = loader.getController();
newController.setStage(dialogStage);
newController.setPerson(person);
dialogStage.show();
myStage.close();
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
Main.java must initialize the Stage and the shared data as follows. Both the Stage and the shared data are passed to the first Scene when it is loaded.
package multiscene;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Person person = new Person();
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("Name.fxml"));
AnchorPane page = (AnchorPane) loader.load();
Stage dialogStage = new Stage();
dialogStage.setTitle("Person Editor");
Scene scene = new Scene(page);
dialogStage.setScene(scene);
NameController nameController = loader.getController();
nameController.setStage(dialogStage);
nameController.setPerson(person);
dialogStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Here is one simple method you might try
import javafx.stage.Stage;
public class ManyScenes extends Application {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(final Stage primaryStage) {
primaryStage.setTitle("Hello World");
Group root1 = new Group();
Group root2 = new Group();
Group root3 = new Group();
final Scene scene1 = new Scene(root1, 300, 250);
final Scene scene2 = new Scene(root2, 300, 250);
final Scene scene3 = new Scene(root3, 300, 250);
Button go1 = new Button();
go1.setLayoutX(100);
go1.setLayoutY(80);
go1.setText("Go to scene2");
go1.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
primaryStage.setScene(scene2);
}
});
root1.getChildren().addAll(new Label("Scene 1"), go1);
Button go2 = new Button();
go2.setLayoutX(100);
go2.setLayoutY(80);
go2.setText("Go to scene3");
go2.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
primaryStage.setScene(scene3);
}
});
root2.getChildren().addAll(new TextField(), go2);
Button go3 = new Button();
go3.setLayoutX(100);
go3.setLayoutY(80);
go3.setText("Back to scene1");
go3.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
primaryStage.setScene(scene1);
}
});
root3.getChildren().addAll(new TextArea(), go3);
primaryStage.setScene(scene1);
primaryStage.show();
}
}