im trying to add some items to my View Table . im creating a ObservableList and pass it to the setItem method but it throws null Exception .
Here is the latest attempt :
javafx.scene.control.TableView table = controller.friendsList;
/*
the controller.friendList is defined as Below :
#FXML
public javafx.scene.control.TableView friendsList;
*/
ObservableList friendData = FXCollections.observableArrayList();
for(Client friend : friends )
{
friendData.add(friend.getUsername());
}
//The friendData is fine ( according to the debugging ) it includes 2 Strings
table.setItems(friendData);
table.setEditable(false);
table.setVisible(false);
table.setVisible(true);
}
whats wrong with the code ?
EDIT:
now i get the controller from the FXMLLoader as the answere says . but it's still the same . all the field with the #FXML anotation are still null .
here is how i get the controller :
public userSceneController controller;
FXMLLoader fxmlLoader = new FXMLLoader();
Parent root = fxmlLoader.load(getClass().getResource("userScene.fxml"));
controller = (userSceneController) fxmlLoader.getController();
Since your TableView is a #FXML-annotated field in the controller, it is initialized in the controller instance created by the FXMLLoader. In other words, presumably at some point in the code you didn't actually show, you create an FXMLLoader and call load() on it. As part of that load process, the FXMLLoader creates an instance of the controller and initializes the #FXML-annotated fields with references to the controls displayed in the UI.
You are presumably intending to configure the table view that is displayed in the UI; in order to do that, you need to reference the controller that the FXMLLoader created for you.
However, what you actually do in your code is to create a new controller instance:
Controller controller = new Controller();
Obviously since this in not the controller created as part of the FXMLLoader.load() process, it never had its friendsList field initialized.
Instead, you need to retrieve the controller instance from your FXML loader. In order to do this, you must create an FXMLLoader with a location properly set, and then use the instance method FXMLLoader.load(), not the static method FXMLLoader.load(URL):
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("userScene.fxml"));
Parent root = (Parent) fxmlLoader.load();
controller = (userSceneController) fxmlLoader.getController();
Related
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 read a few of the related posts on this topic but have been unable to use them to solve my issue. I believe my failure is one of comprehension and not that I am facing a unique issue. But, I am at a total impasse.
I'm building a CRUD application using JavaFX. One of my application's buttons, "Import Data," is throwing NullPointerExceptions:
// *a button that opens a new window with a textField where the user can paste text data*
#FXML
private void importDataButton(ActionEvent event) {
// *load the fxml file*
URL viewLocation = getClass().getResource("/importView.fxml");
// *get the file's controller*
FXMLLoader loader = new FXMLLoader();
ImportController importController = loader.getController();
importController.setMainController(this);
loader.setLocation(viewLocation);
try {
loader.load();
} catch (IOException exception) {
System.out.println("IO Exception thrown.");
return;
}
....
}
I'm not very good with IntelliJ's debugger yet, but I've used it to determine that the FXMLLoader object is null. So when
importController.setMainController(this);
executes, a NullPointerException is thrown because the object this refers to is null. I...think. So then
ImportController importController = loader.getController();
cannot retrieve the controller from the FXMLLoader object (loader).
For reference, setMainController() is in another class called ImportController, and that method's code is as follows:
public void setMainController(MainController mainController) {
this.mainController = mainController;
}
Things I've tried:
I read this post and this post, both of them recommend that I must run loader.load() [given FXMLLoader loader = new FXMLLoader()] in order to retrieve data from the object. However, I've tried this, and I just get errors upon errors: InvocationTargetExceptions and IllegalStateExceptions. I have also tried instantiating an FXMLLoader object that is non-null, using
FXMLLoader load = new FXMLLoader(getClass.getResource("sample.fxml"));
But it seems to have no effect on the content of the object (and yes, I am replacing "sample.fxml" with my filename.)
I hate to make a similar post, but I have no idea what to do.
The problem is that you are calling loader.getController() before loader.load(). That's why your importController is null when calling importController.setMainController(this).
Call loader.load() first:
URL viewLocation = getClass().getResource("/importView.fxml");
FXMLLoader loader = new FXMLLoader(viewLocation);
try {
loader.load();
ImportController importController = loader.getController();
importController.setMainController(this);
} catch (IOException exception) {
exception.printStackTrace();
}
But be aware that the initialize() method in your ImportController is called before setMainController().
I would like to communicate with a FXML controller class at any time, to update information on the screen from the main application or other stages.
Is this possible? I havent found any way to do it.
Static functions could be a way, but they don't have access to the form's controls.
Any ideas?
You can get the controller from the FXMLLoader
FXMLLoader fxmlLoader = new FXMLLoader();
Pane p = fxmlLoader.load(getClass().getResource("foo.fxml").openStream());
FooController fooController = (FooController) fxmlLoader.getController();
store it in your main stage and provide getFooController() getter method.
From other classes or stages, every time when you need to refresh the loaded "foo.fxml" page, ask it from its controller:
getFooController().updatePage(strData);
updatePage() can be something like:
// ...
#FXML private Label lblData;
// ...
public void updatePage(String data){
lblData.setText(data);
}
// ...
in the FooController class.
This way other page users do not bother about page's internal structure like what and where Label lblData is.
Also look the https://stackoverflow.com/a/10718683/682495. In JavaFX 2.2 FXMLLoader is improved.
Just to help clarify the accepted answer and maybe save a bit of time for others that are new to JavaFX:
For a JavaFX FXML Application, NetBeans will auto-generate your start method in the main class as follows:
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
Now, all we need to do to have access to the controller class is to change the FXMLLoader load() method from the static implementation to an instantiated implementation and then we can use the instance's method to get the controller, like this:
//Static global variable for the controller (where MyController is the name of your controller class
static MyController myControllerHandle;
#Override
public void start(Stage stage) throws Exception {
//Set up instance instead of using static load() method
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
Parent root = loader.load();
//Now we have access to getController() through the instance... don't forget the type cast
myControllerHandle = (MyController)loader.getController();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
Another solution is to set the controller from your controller class, like so...
public class Controller implements javafx.fxml.Initializable {
#Override
public void initialize(URL location, ResourceBundle resources) {
// Implementing the Initializable interface means that this method
// will be called when the controller instance is created
App.setController(this);
}
}
This is the solution I prefer to use since the code is somewhat messy to create a fully functional FXMLLoader instance which properly handles local resources etc
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/sample.fxml"));
}
versus
#Override
public void start(Stage stage) throws Exception {
URL location = getClass().getResource("/sample.fxml");
FXMLLoader loader = createFXMLLoader(location);
Parent root = loader.load(location.openStream());
}
public FXMLLoader createFXMLLoader(URL location) {
return new FXMLLoader(location, null, new JavaFXBuilderFactory(), null, Charset.forName(FXMLLoader.DEFAULT_CHARSET_NAME));
}
On the object's loading from the Main screen, one way to pass data that I have found and works is to use lookup and then set the data inside an invisible label that I can retrieve later from the controller class. Like this:
Parent root = FXMLLoader.load(me.getClass().getResource("Form.fxml"));
Label lblData = (Label) root.lookup("#lblData");
if (lblData!=null) lblData.setText(strData);
This works, but there must be a better way.
I have designed a scene using JavaFX Scene Builder/FXML and I want to create many instances of that scene, but each scene with different behavior. Is there a way to change the controller of a scene/FXML dynamically?
What I want is to design one scene and reuse it, but with different behaviors for each instance.
Currently I am loading the FXML and its controller like this:
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(fxmlFile));
Parent root = (Parent) fxmlLoader.load();
Scene scene = new Scene(root);
Controller controller = fxmlLoader.getController();
There is the possibility to use a custom Controllerfactory with the FXML loader.
The setControllerFactory method expects a Callback type, which is an interface with only one callable function: call which the factory uses. The function (called by the FXMLLoader class) expects to get a Class object as an input parameter and provides an instantiated object based on the type. Above Java8 lambdas can be used to provide the factory:
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(fxmlFile));
loader.setControllerFactory((Class<?> controllerType) ->{
if(controllerType == Controller.class){
return new Controller();
}
});
Parent root = (Parent) fxmlLoader.load();
The argument controllerType in the above code is the the type provided by the fxml fx:controller attribute, which is determined by the Java Class loader. When the Controllerfactory is called, nothing is instantiated yet, that's why even abstract classes can be given in the fxml. To achieve different behavior, inheritance can be used.
An example would be:
class Controller{...}
class FirstController extends Controller{...}
class SecondController extends Controller{...}
And the factory can be used like so:
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(fxmlFile));
final int[] instantiatedClasses = {0};
loader.setControllerFactory((Class<?> controllerType) ->{
if(controllerType == Controller.class){
if(0 == instantiatedClasses[0]){
++instantiatedClasses[0];
return new FirstController();
} else return new SecondController();
}
});
Parent root = (Parent) fxmlLoader.load();
Please Note, that this way different arguments can also be supplied to the controller, so inheritance might be an overkill.
For example the primaryStage can be provided to the controller, e.g. for eliminating the need for different setters in the controllers.
class Controller{
public Controller(int behaviorParam){...}
}
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(fxmlFile));
final int[] instantiatedClasses = {-1}; /* so it would start at 0 */
loader.setControllerFactory((Class<?> controllerType) ->{
if(controllerType == Controller.class){
++instantiatedClasses[0];
return new Controller(instantiatedClasses[0]);
}
});
Parent root = (Parent) fxmlLoader.load();
The challenges of this method, however is that it is still not straightforward to differentiate between the different fxml instances of the same controller type. Counting the number of instantiated classes is one way that works, but it doesn't give much control, compared to anything identifier based.
Is there any way of getting the Scene object of an FXML loaded file from the associated class controller.
I'm doing something like this:
#FXML
private AnchorPane anchor;
Scene scene = anchor.getScene();
but i'd like a solution that does not reference the AnchorPane control.
Why not? Controller is an abstract class, he's not aware about UI unless you deliberately make him know.
Nodes (inlcuding AnchorPane) are another story, they hardly exists outside for scenegraph. So it's perfectly fine to ask Node about his parent or scene.
If you still want to handle that separately there are next approaches:
you can create a custom controller and set scene after loader. Just note that at the time initialize() called it wouldn't yet initialized.
public class MyController {
private void Scene scene;
public void setScene(Scene scene) { this.scene = scene; }
}
// loading code
FXMLLoader fxmlLoader = new FXMLLoader();
AnchorPane root = (AnchorPane) fxmlLoader.load(getClass().getResource("MyApp.fxml"));
MyController myController = (MyController) fxmlLoader.getController();
myController.setScene(scene);
You can create a custom fxml control which will incorporate controller and he can just call getScene() for itself. See an example here: https://stackoverflow.com/a/10718683/1054140
I tried your answer, but it did not work, I found the reason here:
JavaFX: How to get stage from controller during initialization?
after the comment:
// loading code
don't use the static load method
AnchorPane root=(AnchorPane) FXMLLoader.load(getClass().getResource("MyApp.fxml"));
but instead use instantiated loader's method
AnchorPane root=(AnchorPane) fxmlLoaded.load(getClass().getResource("MyApp.fxml"));