Reputation: 11
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
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:
Application
subclassinit()
on the Application
subclass (the default implementation is a no-op)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
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