Run method from controller class on startup Javafx - javafx

I got a javafx application and I got a main where I set up my stage and launch the application. I also have a controller class:
public class Controller
{
#FXML Button button;
public void test(){
button.setText("Button");
}
}
How would I go about running the test method on startup. I know I can create an instance in the main class...
public class Main extends Application{
public void start(Stage primaryStage) throws Exception {
...
public static void main (String[] args){
launch (Main.class);
Controller cont = Controller();
cont.test();
}
}
and that would work. However it is not ideal for me. I was wondering if it was possible to run the method directly from the controller class, if not is there a better way of handling this? Thanks

The initialize() method is called automatically when the FXML is loaded:
public class Controller
{
#FXML Button button;
public void initialize(){
button.setText("Button");
}
}
Note that your code in the Main class won't work at all. First, launch() does not exit until you exit the application, and second, you are calling it on a new instance of the controller, not the one that is connected to the UI you load from the FXML file.

Related

JavaFx with Google Guice gives two different instances of Controller

I introduced Google Guice to my JavaFx applicatiomn. However I have some problem when I try to #Inject my controller into another controller.
First of all I do:
loader.setControllerFactory(Main.getInjector()::getInstance);
MainController has TopMenuButtonsController included in fxml file:
<fx:include fx:id="topMenuButtons" source="TopMenuButtons.fxml" />
Then I try to load my MainController using FXMLLoader with Guice Controller Factory. TopMenuButtons are initialized automatically because it's included to MainController.
Finally I try to inject MainController into TopMenuButtonsController (because buttons control what to display in MainController:
public class TopMenuButtonsController {
private MainController mainController;
#Inject
public void setMainController(MainController mainController) {
this.mainController = mainController;
}
#FXML
public void onCreateOrder(ActionEvent event) {
mainController.setCenter(MainController.CREATE_ORDER_FXML);
}
It succeeds but the instance injected here has all field set to null (#FXML annotation didn't work). I also see that this instance injected here is some different one than I use (different object id)
I can simply make it work by doing this in MainController:
#FXML
private void initialize() {
topMenuButtonsController.setMainController(this);
}
but my intention was to get rid of such things and use DI. What I can do?

Null exception when I try to access a controller that I passed using JavaFX

I have a root layout with multiples Tabs. From my main App I open the root layout. There I included multiple FXMLs with their own controllers.
I am trying to pass the main controller to one of the Tabes controller.
The issue I am having, everything works as expected, but I get a null exception when I try to click on an action button from the new tab.
RootLayout FXML
<fx:indlue fx:id="myNewTabAnchorPane" source="NewTabFXML.fxml"/>
RootLayout Controller
#FXML NewTabController newTabController;
mainTabPane.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Tab>(){
#override
public void change(ObservableValue<? extends Tab> observable, Tab oldValue, Tab newValue){
if(newValue == myTab){
newTabController.setMyRootController(this);
}
NewTabController
public void setMyRootController(RootController rootController){
this.rootController = rootController;
System.out.println(rootController.getID); // this prints fine
}
However, if I trigger this action I get blank from the same controller
#FXML
public void createAction(ActionEvent event) throws IOException{
System.out.println(rootController.getID); // with this I get null value.
}
What am I missing?
Here is the problem:
#FXML NewTabController newTabController;
It should be myNewTabAnchorPaneController which is not a partially lowercased class name, but fx:id + Controller concatenation.

JavaFX window setTitle

I have a main class which is the following:
public class Window extends Application {
#Override
public void start(Stage foablak) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("Foablak.fxml"));
Scene scene = new Scene(root);
foablak.setScene(scene);
foablak.setWidth(900);
foablak.setHeight(700);
foablak.setResizable(false);
foablak.setTitle("Window");
foablak.show();
}
public static void main(String[] args) {
launch(args);
}
}
How can I update the Title of the from another .java class without closing the window and open a new one?
Exposing properties using static, just for the sake of exposing, may be considered as a bad design. You have different ways to achieve the same, for example, expose a method from the Window class which sets the stage title.
public class Window extends Application {
private Stage stage;
#Override
public void start(Stage foablak) throws Exception {
stage = foablak;
Parent root = FXMLLoader.load(getClass().getResource("Foablak.fxml"));
Scene scene = new Scene(root);
foablak.setScene(scene);
foablak.setWidth(900);
foablak.setHeight(700);
foablak.setResizable(false);
foablak.setTitle("Window");
foablak.show();
}
public static void main(String[] args) {
launch(args);
}
public void setStageTitle(String newTitle) {
stage.setTitle(newTitle);
}
}
Yes, you can. Inside of your Application.start() method, save a reference to your primary Stage that you can access elsewhere, and then call Stage.setTitle().
class MyApplication extends Application {
public static Stage primaryStage;
#Override
public void start(Stage primaryStage) {
MyApplication.primaryStage = primaryStage;
// ...
}
}
MyApplication.primaryStage.setTitle("New Title");
As an aside, I would avoid calling your class Window, as that is the name of one of the JavaFX classes.
The following may not be the solution you were looking for, but it might be useful for some of the developers:
Scenario: There is only 1 JavaFX app; The app needs to be run
multiple times; You want to differentiate who is running the app
Attention: Pay attention of the running order of the Main and
Controller class
Solution: i. In the Controller Class, declare a private static variable, e.g., private static String strWho; ii. Expose strWho by providing a getter method. e.g.: public static String getWho(){
return strWho;
}; iii. Implement the initialize method for the Controller, and based on your need, assign a distinct value each time you run the JavaFX app. eg., #FXML
public void initialize() {
strWho = "you need to have logic here, to have a distinct value each time you run the app";
}
In the Main start method, right before you call the stage.show, set the title. eg:
primaryStage.setTitle(Controller.getWho()));
primaryStage.show();
One way to implement the logic for distinct value of the strWho each time you run the app: You can have a TextInputDialog in the Controller's initialize method, to accept user input, by asking for a name etc.

I am looking for fxml update on startup

In javafx there are onclickaction , ondragaction, etc
But I cant find something like onstartupaction that perform an action when the application starts
See the Application javadoc for an instance of the application lifecycle. When your application starts, it's start method will be called.
Initialization of fxml is different from application startup, as one application may have many fxml documents loaded many times, each time having a new controller instantiatd and it's initialization method called. This is described in the #FXML Controllers section of the Introduction to FXML documentation.
In the following controller, the initialize method will be invoked by the FXMLLoader. Every time it loads an FXML document referencing the controller class, the loader will create a new controller instance and invoke initialize on it.
public class MyController implements Initializable {
#FXML private Button button;
#FXML
protected void initialize()
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("You clicked me!");
}
});
}
}

javafx fxml is null outside initialize()

In this code:
public class ESM extends Application {
private Stage primaryStage;
#FXML
private ToolBar mainToolBar;
#Override
public void start(final Stage stage) throws Exception {
try{
this.primaryStage = stage;
Parent root = FXMLLoader.load(getClass().getResource("/nz/co/great_ape/esm3/main_window.fxml"));
Scene scene = new Scene(root, 800, 700);
// Setup main stage to be full screen, no min or max buttons.
// TODO: How will this handle multiple screens? Apparently not well :-(
Screen screen = Screen.getPrimary();
Rectangle2D bounds = screen.getVisualBounds();
primaryStage.setX(bounds.getMinX());
primaryStage.setY(bounds.getMinY());
primaryStage.setWidth(bounds.getWidth());
primaryStage.setHeight(bounds.getHeight());
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setTitle("ESM three");
primaryStage.setScene(scene);
primaryStage.show();
System.out.println("This will fail because mainToolBar is null. Why?");
assert mainToolBar != null : "fx:id=\"mainToolBar\" was null check your FXML ";
} catch (Exception ex) {
Logger.getLogger(ESM.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* Use initialize() to setup widgets from scenebuilder files, it is
* called by FXMLLoader.
*/
#FXML
public void initialize(){
System.out.println("initialize() But when here all is good and mainToolBar is a ToolBar.");
assert mainToolBar != null : "fx:id=\"mainToolBar\" was null check your FXML ";
}
/**
* The main() method is ignored in correctly deployed JavaFX application.
* main() serves only as fallback in case the application can not be
* launched through deployment artifacts, e.g., in IDEs with limited FX
* support.
*
* #param args The command line arguments.
*/
public static void main(String[] args) {
launch(args);
}
}
I cant see why it's got a value in the initialise() but in the start it's null. When debuging it's clear that initiialize() is called by FXMLLOader from inside start()
I was going to post the fxml but it does not seem to work as nothig shows in the preview. Any way, it's a real basic file, a BordePane and a ToolBar.
Any clues?
Always create a new class for your FXML Controller, don't try to reuse an Application class as a Controller class.
An Application instance is created by the JavaFX application launcher.
A Controller instance is created by the JavaFX FXML loader.
You don't supply the FXML that you use, but I am going to guess that it has it's Controller class erroneously set to be your application class.
So in your code, what happens is:
An instance of the application is created when you run the program (via the launch method).
In your application start method, you invoke the FXMLLoader, which instantiates a new Controller (in your case a new instance of the application class).
The FXMLLoader injects the #FXML tagged members into the new application object and invokes the initialize on the new object.
But your original application object doesn't know anything about the new application object and hence doesn't have a menu bar set in it.
In summary, to fix this:
Create a new controller class that the FXMLLoader can instantiate.
Change your fxml to reference the new controller class.
If your application really needs to reference the controller, then you can use the getController method on the FXML loader and in your controller class provide public methods to retrieve required elements (like your menu bar). See my answer to Passing Parameters JavaFX FXML for some more examples of this method.
import javafx.scene.control.ToolBar;
import javafx.fxml.FXML;
public class ESMController {
#FXML
private ToolBar mainToolBar;
public ToolBar getMainToolBar() { return mainToolBar; }
#FXML
public void initialize(){
assert mainToolBar != null : "fx:id=\"mainToolBar\" was null check your FXML ";
}
}

Resources