When creating the display in my main loop, the loader for an AnchorPane FXML returns null when getController() is called.
//instantiates the FXMLLoader class by calling default constructor
//creates an FXMLLoader called loader
FXMLLoader loader = new FXMLLoader();
//finds the location of the FXML file to load
loader.setLocation(mainApp.class.getResource("/wang/garage/view/ItemOverview.fxml"));
//sets the AnchorPane in the FXML file to itemOverview
//so that the AnchorPane is set to the display of the app
AnchorPane itemOverview = (AnchorPane) loader.load();
rootLayout.setCenter(itemOverview);
//finds the controller of the itemOverview and
//sets it to controller variable
//then provides a reference of mainApp to controller to connect the two
ItemOverviewController controller = loader.getController();//returns null
controller.setMainApp(this);
I did not specify the controller in the FXML document. Is this necessary if I am using loader.load()? If so, how should I specify the controller in the FXML document?
If you are not setting the controller in Java code directly, you need to specify the controller class in the FXML file (else the FXMLLoader will have no information as to what kind of object it is supposed to create to use as the controller).
Just add the
fx:controller="com.mycompany.myproject.ItemOverViewController
attribute to the root element of the FXML file in the usual way.
Alternatively, you can set the controller from Java:
//instantiates the FXMLLoader class by calling default constructor
//creates an FXMLLoader called loader
FXMLLoader loader = new FXMLLoader();
//finds the location of the FXML file to load
loader.setLocation(mainApp.class.getResource("/wang/garage/view/ItemOverview.fxml"));
// create a controller and set it in the loader:
ItemOverviewController controller = new ItemOverviewController();
loader.setController(controller);
//sets the AnchorPane in the FXML file to itemOverview
//so that the AnchorPane is set to the display of the app
AnchorPane itemOverview = (AnchorPane) loader.load();
rootLayout.setCenter(itemOverview);
//provide a reference of mainApp to controller to connect the two
controller.setMainApp(this);
Related
My dashboard fxml location is Dashboard/DashBoardScene.fxml. I tried to switch from Login/LoginController to dashboard screen
public void onLoginButtonClick(ActionEvent actionEvent) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Dashboard/DashBoardScene.fxml"));
Parent root1 = (Parent) fxmlLoader.load();
Stage stage = new Stage();
stage.initModality(Modality.APPLICATION_MODAL);
stage.initStyle(StageStyle.UNDECORATED);
stage.setTitle("ABC");
stage.setScene(new Scene(root1));
stage.show();
}
but i got location is required error?
The code is in a class in the RestarantApp.Dashboard package (BTW, please use proper naming conventions). Your FXML file is in the same package.
The code getClass().getResource(...) will return a URL of a resource that is searched relative to the current class. Since the current class and the FXML file are in the same package, all you need is
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("DashBoardScene.fxml"));
(This assumes the FXML file is being deployed correctly, the resource name is spelled correctly, etc.)
You can also specify an "absolute" path (one that is relative to the classpath) to the resource:
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/RestarantApp/Dashboard/DashBoardScene.fxml"));
Note here the path begins with a leading /.
with the ScheneBuilder I defined the controller class of my fxml, the code genereted inside my AnchorPane tag is:
fx:controller="demo.SplashController"
now I would like if I had args in the main, to load a new version of the controller, using the appropriate construct. I try this code in the Application.start:
FXMLLoader loader = new FXMLLoader(getClass().getResource("page.fxml"));
PageController controller;
if(!dir.equals("")){ //attribute coming from args
controller = new PageController(dir);
}else{
controller = new PageController();
}
loader.setController(controller);
AnchorPane root = loader.load();
Scene scene = new Scene(root,480,414);
primaryStage.setScene(scene);
primaryStage.show();
but using this code a conflict appears because I have already defined the controller in my project with FXML code, to solve it would be enough to remove the segment in the FXML code but I would not do it because leaving the code in the fxml allows me to access some good features of the SceneBuilder.
The only way pass parameters to the controller's constructor and specify the controller's class in the fxml is to use a controller factory:
FXMLLoader loader = new FXMLLoader(getClass().getResource("page.fxml"));
loader.setControllerFactory(cl -> dir.isEmpty() ? new PageController() : new PageController(dir));
AnchorPane root = loader.load();
Another option would be to create a method in the controller class that allows you to pass the info after loading and does the initialisation:
FXMLLoader loader = new FXMLLoader(getClass().getResource("page.fxml"));
AnchorPane root = loader.load();
PageController controller = loader.getController();
controller.setDir(dir);
Note that the method call happens after the initialize method is run assuming there is one.
I used some commands for getting the controller of a fxml file. at first I used an address like this:
fx:controller="PersonOverviewController"
and the code in main class was like this
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/PersonOverview.fxml"));
AnchorPane ap = loader.load();
PersonOverviewController pc = loader.getController();
pc.setTableContent(this);
but it doesn't work.
in another attempt, I changed the
fx:controller="address.view.PersonOverviewController"
and this time it worked.
Why this is the case?
FXMLLoader needs the binary name of the controller class to be specified. If the package of your PersonOverviewController is address.view you therefore have to include it in the attribute value.
FXMLLoader basically creates the controller instance like this, if no controller factory is set:
String fxController = ...
Class controllerClass = getClassLoader().loadClass(fxController);
Object controller = controllerClass.newInstance();
I am having the following problem with a program that I am currently writing, and I have searched on the internet, but I couldn't really find anything to help me understand the following problem
So inside another class I have written a method that executes this whenever the search button is clicked and the method looks like this:
public void searchButton(){
try {
new SearchController().display();
} catch (IOException e) {
e.printStackTrace();
}
}
And then the SearchController class looks something like this (I simplified it here):
public class SearchController {
#FXML
private Button cancelButton;
#FXML
private Label what;
private static Stage stage;
private static BorderPane borderPane;
#FXML
public void initialize(){
what.setText("Testing"); // this woks
cancelButton.setOnAction(e -> stage.close());
}
public void display() throws IOException {
stage = new Stage();
stage.setResizable(false);
stage.setTitle("Product search");
stage.initModality(Modality.APPLICATION_MODAL);
FXMLLoader loader = new FXMLLoader();
loader.setLocation(SearchController.class.getResource("Search.fxml"));
borderPane = loader.load();
Scene scene = new Scene(borderPane);
stage.setScene(scene);
//what.setText("Testing") and this doesn't work
stage.showAndWait();
}
}
Can someone please tell me why it is possible to write text on the initialize method (that method gets called after the borderPane = loader.load(); line...so why doesn't it work if I try to write on the label after that line?)
Thank you in advance
The FXMLLoader creates an instance of the class specified in the fx:controller attribute of the FXML root element. It then injects the elements defined in the FXML file into the controller instance it created when the fx:id attributes match the field names. Then it calls the initialize() method on that instance.
You create an instance of the controller "by hand" with new SearchController(). This is not the same object that is created by the FXMLLoader. So now when you have loaded the fxml file you have two different instances of SearchController. So if you call what.setText(...) from the display() method, you are not calling it on the controller instance created by the FXMLLoader. Consequently, what has not been initialized in the instance on which you are calling what.setText(...), and you get a null pointer exception.
Since initialize() is invoked by the FXMLLoader on the instance it created, when you call what.setText(...) from the initialize() method, you are calling it on the instance created by the FXMLLoader, and so the FXML-injected fields for that instance have been initialized.
I have a main FXML document for my program which contains a TabPane. For each tab I want it to have its own controller and fxml file. When I try to include the external fmxl files into the main fxml document, my program refuses to run. here is my main FXML document:
here is a copy of my java file
#Override
public void start(Stage stage) throws Exception {
FXMLLoader fxml = new FXMLLoader();
Parent root = fxml.load(getClass().getResource("FXMLDocument.fxml").openStream());
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
FXMLDocumentController fdc = fxml.getController();
}
Error:
Caused by: javafx.fxml.LoadException: Base location is undefined. unknown path:97
This error is caused because you have not set the location property of the FXMLLoader, and instead you are specifying an InputStream from which to load the FXML. I think the FXMLLoader must need to know the location of the original fxml file in order to resolve the location of the included file. You should really only use the load(InputStream) method in exceptional circumstances: when you are loading the fxml from a source other than a resource (i.e. file or resource in your application jar file).
Instead, use
FXMLLoader fxml = new FXMLLoader();
fxml.setLocation(getClass().getResource("FXMLDocument.fxml"));
Parent root = fxml.load();
or, equivalently,
FXMLLoader fxml = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
Parent root = fxml.load();