ngnear
ngnear

Reputation: 11

Using a parameter in JavaFX methods

I am building an JavaFX application and am want to access values passed as an argument within the JavaFX UI. For some reason, I am unable to access these values in all the methods except the base method launchForm. Here is what my code looks like.

public class FormBuilder extends Application {

    /*
     * (non-Javadoc)
     * @see javafx.application.Application#start(javafx.stage.Stage)
     * Scene scene
     *      Group root
     *          BorderPane borderPane
     *              TabPane tabPane
     *                  Tab stocksTab
     *                      BorderPane stockTabBorderPane
     *                          GridPane gridPane
     *          
     */

    private Stocks stockData = new Stocks();
    private int size;

    @Override
    public void start(Stage stage) throws Exception {
        stage.setTitle("Stock Manager");
        Group root = new Group();
        Scene scene = new Scene(root, 1024, 800, Color.WHITE);

        TabPane tabPane = new TabPane();
        BorderPane borderPane = new BorderPane();
        BorderPane stockTabBorderPane = new BorderPane();

        Tab stocksTab = new Tab("Stocks");

        stockTabBorderPane.setTop(this.addHBox());
        stockTabBorderPane.setCenter(this.createGridPane());

        stocksTab.setContent(stockTabBorderPane);
        tabPane.getTabs().add(stocksTab);

        borderPane.setCenter(tabPane);

        borderPane.prefHeightProperty().bind(scene.heightProperty());
        borderPane.prefWidthProperty().bind(scene.widthProperty());

        root.getChildren().add(borderPane);


        stage.setScene(scene);
        stage.show();


    }

    private HBox addHBox() {
        HBox hbox = new HBox();
        hbox.setPadding(new Insets(15, 12, 15, 12));
        hbox.setSpacing(10);
        hbox.setStyle("-fx-background-color: #336699;");

        Button buttonCurrent = new Button("Current");
        buttonCurrent.setPrefSize(100, 20);

        Button buttonProjected = new Button("Projected");
        buttonProjected.setPrefSize(100, 20);
        hbox.getChildren().addAll(buttonCurrent, buttonProjected);

        return hbox;
    }

    private GridPane createGridPane() {

        GridPane gridPane = new GridPane();

        gridPane.setLayoutX(39);
        gridPane.setLayoutY(131.0);
        gridPane.setAlignment(Pos.TOP_CENTER);
        gridPane.setVgap(5.0);
        gridPane.setHgap(10.0);

        gridPane.add(new Label("Active"), 1,1);
        gridPane.add(new Label("Stock"), 2, 1);
        gridPane.add(new Label("Symbol"), 3, 1);
        gridPane.add(new Label("LPP"), 4, 1);
        gridPane.add(new Label("LPP"), 5, 1);
        gridPane.add(new Label("HPP"), 6, 1);
        gridPane.add(new Label("LTP"), 7, 1);

        System.out.println(this.size);
        for(int v=2;v < this.stockData.getStocks().size()+2; v++) {
            gridPane.add(new CheckBox(), 1, v);
            gridPane.add(new Label("Amazon"), 2, v);
            gridPane.add(new TextField (), 3,v);
            gridPane.add(new TextField (), 4,v);
            gridPane.add(new TextField (), 5,v);
            gridPane.add(new TextField (), 6,v);
            gridPane.add(new TextField (), 7,v);

        }

        return gridPane;
    }

    public void launchForm(Stocks stockData) {
        this.stockData = stockData;
        this.size = stockData.getStocks().size();
        System.out.println(stockData.getStocks().size());
        System.out.println(stockData.getStocks().get(0).getSector());
        launch();
    }
}

Now the issue is that when I try and access any value under the stockData object within the createGridPane method, the values are not available.

Examples are

this.stockData.getStocks().size() gives the value of 0 in the createGridPane method. But it gives a value of 2 in the launchForm method.

Again there are other values like

 this.stockData.getStocks().get(0).getSector()

which returns the value "Retail" in the launchForm method. But when I try to access the same in a different method in the same class, I get an exception.

Can someone please help me here?

Upvotes: 0

Views: 414

Answers (2)

James_D
James_D

Reputation: 209330

In JavaFX you should basically consider the Application subclass, and in particular its start() method, to be the entry point of the application. The application lifecycle is described in the Application Javadocs, but in brief the JavaFX launch process is initiated either by calling one of the static Application.launch(...) methods, or (using the Oracle JDK) by launching the JVM and specifying an Application subclass as the main class (even if it doesn't have a main method).

The launch process then:

  1. starts the JavaFX toolkit
  2. creates a new instance of the Application subclass
  3. invokes init() on the Application subclass (the default implementation is a no-op)
  4. starts the FX Application Thread
  5. invokes start() on the Application subclass, executing it on the FX Application Thread.

Despite being invoked on different threads, start() is guaranteed not to be invoked until init() has completed.

From the code you posted, it must be the case that you are instantiating your FormBuilder class somewhere else, and calling launchForm(...) on that instance. When you call launch() from there, that creates a second instance and invokes start() on it, as described above. So of course the fields that you set on the instance on which launchForm(...) is called will not be set on the instance on which start(...) is called.

You should refactor your code so that either FormBuilder is the entry point to the application, or make FormBuilder not be an Application subclass and create a new entry point that instantiates and uses it. It appears you have some background work which loads data: this should be a separate class which should not be the entry point. So the first refactoring would look like:

// class that reads data and encapsulates it as a Stocks object

public class StockDataAccessor {

    // ...

    public Stocks getStocks() {
        // ...
    }

}

Then FormBuilder looks like:

public class FormBuilder extends Application {

    /*
     * (non-Javadoc)
     * @see javafx.application.Application#start(javafx.stage.Stage)
     * Scene scene
     *      Group root
     *          BorderPane borderPane
     *              TabPane tabPane
     *                  Tab stocksTab
     *                      BorderPane stockTabBorderPane
     *                          GridPane gridPane
     *          
     */

    private Stocks stockData ;
    private int size;

    @Override
    public void start(Stage stage) throws Exception {

        StockDataAccessor stockDataAccessor = new StockDataAccessor();
        stockData = stockDataAccessor.getStocks();

        stage.setTitle("Stock Manager");
        Group root = new Group();
        Scene scene = new Scene(root, 1024, 800, Color.WHITE);

        TabPane tabPane = new TabPane();
        BorderPane borderPane = new BorderPane();
        BorderPane stockTabBorderPane = new BorderPane();

        Tab stocksTab = new Tab("Stocks");

        stockTabBorderPane.setTop(this.addHBox());
        stockTabBorderPane.setCenter(this.createGridPane());

        stocksTab.setContent(stockTabBorderPane);
        tabPane.getTabs().add(stocksTab);

        borderPane.setCenter(tabPane);

        borderPane.prefHeightProperty().bind(scene.heightProperty());
        borderPane.prefWidthProperty().bind(scene.widthProperty());

        root.getChildren().add(borderPane);


        stage.setScene(scene);
        stage.show();


    }

    private HBox addHBox() {
        HBox hbox = new HBox();
        hbox.setPadding(new Insets(15, 12, 15, 12));
        hbox.setSpacing(10);
        hbox.setStyle("-fx-background-color: #336699;");

        Button buttonCurrent = new Button("Current");
        buttonCurrent.setPrefSize(100, 20);

        Button buttonProjected = new Button("Projected");
        buttonProjected.setPrefSize(100, 20);
        hbox.getChildren().addAll(buttonCurrent, buttonProjected);

        return hbox;
    }

    private GridPane createGridPane() {

        GridPane gridPane = new GridPane();

        gridPane.setLayoutX(39);
        gridPane.setLayoutY(131.0);
        gridPane.setAlignment(Pos.TOP_CENTER);
        gridPane.setVgap(5.0);
        gridPane.setHgap(10.0);

        gridPane.add(new Label("Active"), 1,1);
        gridPane.add(new Label("Stock"), 2, 1);
        gridPane.add(new Label("Symbol"), 3, 1);
        gridPane.add(new Label("LPP"), 4, 1);
        gridPane.add(new Label("LPP"), 5, 1);
        gridPane.add(new Label("HPP"), 6, 1);
        gridPane.add(new Label("LTP"), 7, 1);

        System.out.println(this.size);
        for(int v=2;v < this.stockData.getStocks().size()+2; v++) {
            gridPane.add(new CheckBox(), 1, v);
            gridPane.add(new Label("Amazon"), 2, v);
            gridPane.add(new TextField (), 3,v);
            gridPane.add(new TextField (), 4,v);
            gridPane.add(new TextField (), 5,v);
            gridPane.add(new TextField (), 6,v);
            gridPane.add(new TextField (), 7,v);

        }

        return gridPane;
    }

    // for non-JavaFX aware environments (like your IDE...)
    public static void main(String[] args) {
        launch(args);
    }
}

Then launching FormBuilder as your main class will do what you need.


If you want to factor the application entry point out of the FormBuilder class entirely, the alternative refactoring (which is pretty similar) looks like:

public class FormBuilder {

    /*
     * (non-Javadoc)
     * @see javafx.application.Application#start(javafx.stage.Stage)
     * Scene scene
     *      Group root
     *          BorderPane borderPane
     *              TabPane tabPane
     *                  Tab stocksTab
     *                      BorderPane stockTabBorderPane
     *                          GridPane gridPane
     *          
     */

    private Stocks stockData ;
    private int size;

    private Group root ;

    public FormBuilder() {

        StockDataAccessor stockDataAccessor = new StockDataAccessor();
        stockData = stockDataAccessor.getStocks();

        root = new Group();

        TabPane tabPane = new TabPane();
        BorderPane borderPane = new BorderPane();
        BorderPane stockTabBorderPane = new BorderPane();

        Tab stocksTab = new Tab("Stocks");

        stockTabBorderPane.setTop(this.addHBox());
        stockTabBorderPane.setCenter(this.createGridPane());

        stocksTab.setContent(stockTabBorderPane);
        tabPane.getTabs().add(stocksTab);

        borderPane.setCenter(tabPane);

        borderPane.prefHeightProperty().bind(scene.heightProperty());
        borderPane.prefWidthProperty().bind(scene.widthProperty());

        root.getChildren().add(borderPane);



    }

    public Parent getView() {
        return root ;
    }

    private HBox addHBox() {
        HBox hbox = new HBox();
        hbox.setPadding(new Insets(15, 12, 15, 12));
        hbox.setSpacing(10);
        hbox.setStyle("-fx-background-color: #336699;");

        Button buttonCurrent = new Button("Current");
        buttonCurrent.setPrefSize(100, 20);

        Button buttonProjected = new Button("Projected");
        buttonProjected.setPrefSize(100, 20);
        hbox.getChildren().addAll(buttonCurrent, buttonProjected);

        return hbox;
    }

    private GridPane createGridPane() {

        GridPane gridPane = new GridPane();

        gridPane.setLayoutX(39);
        gridPane.setLayoutY(131.0);
        gridPane.setAlignment(Pos.TOP_CENTER);
        gridPane.setVgap(5.0);
        gridPane.setHgap(10.0);

        gridPane.add(new Label("Active"), 1,1);
        gridPane.add(new Label("Stock"), 2, 1);
        gridPane.add(new Label("Symbol"), 3, 1);
        gridPane.add(new Label("LPP"), 4, 1);
        gridPane.add(new Label("LPP"), 5, 1);
        gridPane.add(new Label("HPP"), 6, 1);
        gridPane.add(new Label("LTP"), 7, 1);

        System.out.println(this.size);
        for(int v=2;v < this.stockData.getStocks().size()+2; v++) {
            gridPane.add(new CheckBox(), 1, v);
            gridPane.add(new Label("Amazon"), 2, v);
            gridPane.add(new TextField (), 3,v);
            gridPane.add(new TextField (), 4,v);
            gridPane.add(new TextField (), 5,v);
            gridPane.add(new TextField (), 6,v);
            gridPane.add(new TextField (), 7,v);

        }

        return gridPane;
    }

}

and then create an entry point:

public class StockApp extends Application {

    @Override
    public void start(Stage stage) {
        FormBuilder formBuilder = new FormBuilder();
        Scene scene = new Scene(formBuilder.getView(), 1024, 800, Color.WHITE);
        stage.setTitle("Stock Manager");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Upvotes: 0

fabian
fabian

Reputation: 82461

You're invoking Application.launch in the launchForm instance method and expect it to use the instance this method is invoked for as application class.

The JavaFX launch does not work this way however.

If Application.launch is called, a new instance of the class the method is invoked from is created by the launch method itself and it is this new instance that is used with init and start.

The easiest way to fix this would be, if you could create the Stocks in the init or start (possibly passing some Strings as parameters to launch).

Otherwise you need some other way to communicate with the newly created instance of the Application subclass, e.g. static members...

Upvotes: 1

Related Questions