Issue
I have a onExitButtonClick
FXML method in the MenuController.java
, how can I access the same FXML method in GameOverController.java
? One of the exit button is in Menu scene and the other is in Game Over scene.
MenuController.java
@FXML
private void onExitButtonClick() {
System.out.println("Goodbye " + System.getProperty("user.name"));
System.exit(0);
}
GameOverController.java
@FXML
private void onExitButtonClick() {
System.out.println("Goodbye " + System.getProperty("user.name"));
System.exit(0);
}
Is there anyway to reduce the redundancy of code?
Solution
Specifically answering the question title
How to access FXML method from another controller
Don’t.
Controllers should not need to invoke methods on each other, outside the specific case of:
Even, then, it is not the best practice for most applications.
Best practice is to use shared model classes and service logic outside controllers using mvc. The controller logic should be confined to managing the view and linking the view model via listeners and binding to the object model.
For shared UI components, create custom widget classes and reuse those. See for instance, the Oracle tutorial:
The use of fxml in custom controls is optional, but the concept is similar to the tutorial. You could write your custom control in plain Java. Also it is usually sufficient to have it extend a pane rather than Control, as that is easier.
In the general case, use mvc
If you want to refactor, a reasonable design is to not do app logic in the controllers but instead have a shared domain level service layer, separate from the UI and controllers which works on a shared object model.
For info see:
- Applying MVC With JavaFx
- Eden coding mvc in JavaFX.
That is a decent model for medium to large applications, e.g. four or more screens, but can introduce unnecessary abstractions for very small applications which are just one or two screens. So it depends on the app as to whether it is worth it.
Specific app shutdown case: Application static method
Your particular case though of exiting the application is a special one which wouldn’t apply in most other situations. What is special is that exiting is a truly application level function and there is already a specific class which handles the application lifecycle. That would be your class that subclasses Application. So, it would only make sense that the code to handle exit would go there.
To understand more about the application lifecycle, I strongly advise you to re-read the JavaFX Application javadoc.
There is only one instance of an application allowed in the JVM, so it is ok to access that instance from anywhere in your app using static methods.
So, with that knowledge, we can refactor your calls. In any controller or anywhere else that you want to shutdown your app, write:
@FXML
private void onExitButtonClick() {
MyApplication.exit();
}
And in your application class define a static method:
public static void exit() {
System.out.println("Goodbye " + System.getProperty("user.name"));
Platform.exit();
}
Note that a call is made to exit the platform rather than the system, because that is the recommended way in the JavaFX application lifecycle to shutdown the application. You could pass a parameter to the exit call if you wanted to provide some context for the exit (e.g an exit reason message for instance).
This method of invoking a static function in the application could also be used for other shared logic.
Outside of small trivial apps, I’d only recommend this approach for logic directly related to the application lifecycle.
Techniques like mvc, shared object models with bindable properties, and dependency injection are recommended for general logic in larger apps.
Specific app shutdown case: Application lifecycle integration
Because this is a special case of working with the application life cycle, you could integrate more closely with that and remove the additional static method altogether. If you do that, then you get this code, to just call Platform.exit() whenever you want to exit:
@FXML
private void onExitButtonClick() {
Platform.exit();
}
And in your JavaFX application class code:
@Override
public void stop() {
System.out.println("Goodbye " + System.getProperty("user.name"));
}
This works because the stop() method is a callback invoked when the platform is exited.
Incidentally, this is why you should call Platform.exit and not System.exit. If you exit the system directly, it will short circuit the platform shutdown logic, and the stop logic will not be invoked.
Answered By - jewelsea
Answer Checked By - Marilyn (JavaFixing Volunteer)