How do I access a public String that's initialized in my Main Class from an FXML Controller [duplicate] - java

This question already has an answer here:
Passing Parameters JavaFX FXML
7 answers
In my main class I have
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
public String versionNumber = "v2.1";
#Override
public void start(Stage primaryStage) throws Exception{
// SETTING UP THE STAGE
Stage window;
window = primaryStage;
window.setTitle("Support Tool " + versionNumber);
// SETTING UP THE SCENES
Parent parentNewCallDetails = FXMLLoader.load(getClass().getResource("newCallDetails.fxml"));
Scene scnNewCallDetails = new Scene (parentNewCallDetails, 800, 600);
// CHOOSING THE SCENE AND SHOWING THE STAGE
window.setScene(scnNewCallDetails);
window.show();
}
}
I essentially want to be able to access String versionNumber from within the following code in my FXML controller where I set the title of the next scene that I'm launching
public class newCallController {
// ACTION COMPLETED WHEN CALL BUTTON IS PRESSED
public void btnCall(MouseEvent event) throws IOException {
// TODO LAUNCH THE NEXT CALL WINDOW
Parent fxmlMainCallWindow = FXMLLoader.load(getClass().getResource("mainCallWindow.fxml"));
Scene scnMainCallWindow = new Scene(fxmlMainCallWindow, 1000, 800);
Stage window = (Stage)((Node)event.getSource()).getScene().getWindow();
// THIS IS WHERE I WANT TO ACCESS THE VERSIONNUMBER STRING
window.setTitle("Support Tool " + versionNumber);
window.setScene(scnMainCallWindow);
}
}

You can define a Constants class with static string param. You can use it anywhere. Like: Constants.VERSION
public class Constants {
public static final String VERSION = "v2.1";
}

Related

How to initialize JavaFX application dynamically, not hardcoded?

In many samples it is shown how to extend Application method to have JavaFX app composed and ran.
But what if I don't want to? What if I want to configure app dynamically from my code? Example is below:
import javafx.application.Application;
import javafx.stage.Stage;
public class HollowTry {
public static class HollowApplication extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
}
}
public static void main(String[] args) {
Application.launch(HollowApplication.class, args);
// now I want to set title, scene etc... how?
}
}
Please don't dispute on why I need it.
UPDATE
Okay, launch() is never terminated, I didn't check this. Anyway, I need to have a way to build application programmatically, without any hardcoding.
UPDATE 2
I was wishing con build application from Spring and I found the following solution for now.
JavaFX wrapper class
It wraps context initialization into FX thread and captures config classes to be accessible from start():
public class SpringJavaFX extends Application {
private static Class<?>[] annotatedClasses;
#Override
public void start(Stage primaryStage) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(annotatedClasses);
String title = (String) context.getBean("primaryStageTitle");
primaryStage.setTitle(title);
Scene scene = (Scene) context.getBean("primaryStageScene");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void launch(Class<?>... annotatedClasses) {
SpringJavaFX.annotatedClasses = annotatedClasses;
Application.launch();
}
}
Spring way building
And here is an example of spring-way building. Each component is a bean and created in place:
public class Attempt01_HelloWorld {
#Configuration
public static class Config {
#Bean
String primaryStageTitle() {
return "Attempt01_HelloWorld";
}
#Bean
Scene primaryStageScene() {
Scene ans = new Scene(root(), 800, 600);
return ans;
}
#Bean
Button button() {
Button ans = new Button();
ans.setText("Say 'Hello World'");
ans.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
root().getChildren().add(ans);
return ans;
}
#Bean
StackPane root() {
StackPane root = new StackPane();
return root;
}
}
public static void main(String[] args) {
SpringJavaFX.launch(Config.class);
}
}
I'm not sure it would work, but you could try and add setter methods for the app's parameters inside the inner class, and try and call them from outside (e.g. from your main). Again, I don't know whether this would work or not, but I'd give it a try in your place.

How to access a JavaFx Stage from a Controller?

I'm converting a pure JavaFx app, in which the code below worked fine when put all in one class, to a FXML one, where the Stage declaration and the button handler are in separate classes. In the a Controller, I'm trying to implement a method that will allow the user to choose a directory and store it in a variable for later use:
private File sourceFile;
DirectoryChooser sourceDirectoryChooser;
#FXML
private void handleSourceBrowse() {
sourceDirectoryChooser.setTitle("Choose the source folder");
sourceFile = sourceDirectoryChooser.showDialog(theStage);
}
However, "theStage", a Stage which the method requires, only exists(if that's the right terminology) in FolderSyncer4.java:
public class FolderSyncer4 extends Application {
final String FOLDER_SYNCER = "FolderSyncer";
Stage theStage;
#Override
public void start(Stage primaryStage) throws Exception {
theStage = primaryStage;
//TODO do the FXML stuff, hope this works
Parent root = FXMLLoader.load(getClass().getResource("FolderSyncerMainWindow.fxml"));
theStage.setScene(new Scene(root, 685, 550));
theStage.setTitle(FOLDER_SYNCER);
theStage.show();
}
}
How to I get around this? I need to have that method implemented again somehow, but suddenly I can't pass the stage as an argument.
In your situation, it is probably easiest to get the scene from the ActionEvent parameter of your handler:
#FXML
private void handleSourceBrowse(ActionEvent ae) {
Node source = (Node) ae.getSource();
Window theStage = source.getScene().getWindow();
sourceDirectoryChooser.showDialog(theStage);
}
See JavaFX: How to get stage from controller during initialization? for some more information. I am not in favor of the highest rated answer though, since it adds a compile time dependency to the controller after the .fxml file has been loaded (after all that question was tagged with javafx-2, so not sure if the above approach already worked there, and also the context of the question looks a bit different).
See also How do I open the JavaFX FileChooser from a controller class?
Another way is define a static getter for the Stage and Access it
Main Class
public class Main extends Application {
private static Stage primaryStage; // **Declare static Stage**
private void setPrimaryStage(Stage stage) {
Main.primaryStage = stage;
}
static public Stage getPrimaryStage() {
return Main.primaryStage;
}
#Override
public void start(Stage primaryStage) throws Exception{
setPrimaryStage(primaryStage); // **Set the Stage**
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
}
Now you Can access this stage by calling
Main.getPrimaryStage()
In Controller Class
public class Controller {
public void onMouseClickAction(ActionEvent e) {
Stage s = Main.getPrimaryStage();
s.close();
}
}

How to add a method to a button to switch between scenes in JavaFX

I have created two java classes which have a static method which returns an AnchorPane after setting all properties of required labels and buttons.
For example:
class HomePageScene {
static AnchorPane getHomePageScene() {
//some code
//a button which is to be clicked to go to Login Page
//some code
}
}
class LoginPageScene {
static AnchorPane getLoginPageScene() {
//some code
}
}
And there is another class which has the main().
public class JavaFXEventDemo extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage myStage) {
myStage.setTitle("Program Windiw");
AnchorPane HomePane = HomePageScene.getHomePageScene();
AnchorPane LoginPane = LoginPageScene.getLoginPageScene();
Scene HomePage = new Scene(HomePane, 400.0, 300.0);
Scene LoginPage = new Scene(LoginPane, 400.0, 300.0);
myStage.setScene(HomePage);
myStage.show();
}
}
First I set the HomePage as the scene on the stage. In the screen there is a button, which when I click, I want the scene to the LoginPage. How do I do this?
All the three classes are in different files.I tried setting onAction() method, but in that, handle() method's return type is void, whereas I need to return an AnchorPane.
Bind a function for your button (onAction). In this function, call a function in your main class which will load the scene you want (void javafx.scene.Scene.setRoot(Parent value)) ?
EDIT:
What I meant :
public class JavaFXEventDemo extends Application {
private static Scene HomePage;
private static Scene LoginPage;
private static Stage myStage;
public static void main(String[] args) {
launch(args);
}
public void start(Stage myStage) {
JavaFXEventDemo.myStage = myStage;
myStage.setTitle("Program Windiw");
AnchorPane HomePane = HomePageScene.getHomePageScene();
AnchorPane LoginPane = LoginPageScene.getLoginPageScene();
HomePage = new Scene(HomePane, 400.0, 300.0);
LoginPage = new Scene(LoginPane, 400.0, 300.0);
loadHomePage();
myStage.show();
}
public static void loadHomePage(){
JavaFXEventDemo.myStage.setScene(HomePage);
}
public static void loadLoginPage(){
JavaFXEventDemo.myStage.setScene(LoginPage);
}
}
And just call loadXXXXPage() on your button.

Passing a parameter to JavaFx

I am trying to build a simple image viewer with JavaFx which would work similar to this:
Viewer viewer = new Viewer("path/to/file.jpg");
I've tried something along the lines of the code below, but it does not work.
public class Viewer extends Application {
private String filePath;
public Viewer(String filePath) {
this.filePath = filePath;
}
#Override
public void start(Stage stage) {
// load the image
Image image = new Image("file:" + this.filePath);
// simple displays ImageView the image as is
ImageView iv1 = new ImageView();
iv1.setImage(image);
Group root = new Group();
Scene scene = new Scene(root);
HBox box = new HBox();
box.getChildren().add(iv1);
root.getChildren().add(box);
stage.setTitle(this.filePath);
stage.setWidth(415);
stage.setHeight(200);
stage.setScene(scene);
stage.sizeToScene();
stage.show();
}
}
Is there a standard way of passing parameters to a JavaFx application?
If I understand your question you have pass one or more parameters for your SubClass of Application. The Class Abstract Application have a method called launch that receive a String[] args. Then you can pass a parameter for this, eg. String[]{"--nameOfParameters=value of patameters",...} . You get the paramaters for getParameters().getNamed().get("name of parameters").
A below I put a example.
public class Viewer extends Application {
#Override
public void start(Stage stage) {
// load the image
Image image = new Image("file:" + getParameters().getNamed().get("file"));
...
}
public void caller(String[] args) {
launch(args);
}
/**
* This is a example of the passing a parameters
* #param args the command line arguments
*/
public static void main(String[] args) {
(new Viewer()).caller(new String[]{"--file=path/to/file.jpg"});
}
}
You could simply pass an unnamed parameter
Parameters parameters = getParameters();
List<String> unnamedParameters = parameters.getUnnamed();
filePath = unnamedParameters.get(0); // assumes path/to/file.jpg has been passed

How do I change variable values in an javafx application after it has been instantiated?

I'm trying to make a generic class that I can use for future projects. It just makes a simple javafx browser. The issue I'm having is that I want to be able to change some of the properties dynamically (on instantiation). I added some simple setters hoping it would to the job, but they do not work. Is there a way to change the variables after start() has been executed?
Class code:
package rob.rushton;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class RushBrowser extends Application {
public RushBrowser() {}
private String url = "www.google.com";
private final String fullUrl = "http://" + url;
String title = "Simple Browser";
private int height = 750;
private int width = 750;
public void openBrowser() {
launch();
}
public void setURL(String u) {
url = u;
}
public void setHeightWidth(int h, int w) {
height = h;
width = w;
}
public void setTitle(String t) {
title = t;
}
#Override
public void start(Stage stage) {
stage.setTitle(this.title);
BorderPane pane = new BorderPane();
Scene scene = new Scene(pane, width, height);
WebView browser = new WebView();
WebEngine engine = browser.getEngine();
engine.load(fullUrl);
pane.setCenter(browser);
stage.setScene(scene);
stage.show();
}
}
And I was trying to run it like this:
package rushtest;
import rob.rushton.RushBrowser;
public class RushTest {
public static void main(String[] args) {
RushBrowser rush = new RushBrowser();
rush.setTitle("Test Title");
rush.setURL("www.github.com");
rush.setHeightWidth(1000, 1000);
rush.openBrowser();
}
}
EDIT: (8/9/15) None of the listed suggestions below have worked :( The problem is that I do not know how to access the application thread that is started by launch()
You should either make those properties true JavaFX properties, or update their setters delegate to the actual UI objects. Some rough code - only the relevant parts shown:
Case of true JavaFX properties:
public class RushBrowser extends Application {
...
private StringProperty titleProperty = new SimpleStringProperty("Simple Browser");
...
public void setTitle(String t) {
titleProperty.set(t);
}
#Override
public void start(Stage stage) {
stage.titleProperty().bind(this.titleProperty);
...
}
}
Case of delegation - you also have to keep a reference of the Stage:
public class RushBrowser extends Application {
...
private Stage primaryStage;
// no need to keep the title member variable
...
public void setTitle(String t) {
primaryStage.setTitle(t);
}
#Override
public void start(Stage stage) {
this.primaryStage = stage;
...
}
}
EDIT
As per the comment from James_D, there is another problem: the main method has no reference to the instance created by Application.launch(). So:
If you want to customize the parameters of your application before it starts, you can override Application.init():
public class SpecialRushBrowser extends RushBrowser {
public void init() {
this.setTitle("Test Title");
...
}
}
Or from the test code:
public class RushTest {
static class TestRushBrowser extends RushBrowser {
public void init() {
super.init(); // just in case
this.setTitle("Test Title");
...
}
}
public static void main(String[] args) {
TestRushBrowser.launch();
}
}
If you do not need to modify these parameters later, you can leave your code as is (i.e. no JavaFX properties are required). Otherwise, apply the changes mentioned above.
If you want to change the parameters after the application has started, you need to provide a reference to the actual instance of RushBrowser created by Application.launch() to the code that will execute the changes. A simple but dirty way is with a global variable:
public class RushBrowser extends Application {
public static RushBrowser INSTANCE;
public void init() {
INSTANCE = this;
}
...
}
And then from any code that runs after launch():
RushBrowser.INSTANCE.setTitle(...);
...
As global state is generally dangerous, you might want to try with a dependency injection framework, if the application gets more complex. Even with DI though it can get tricky because the main class is still created from JavaFX, outside the DI framework - but that's another story.
Again you need to apply the changes above the EDIT.
The reason the code you posted doesn't work is that the (static) launch(...) method creates a new instance of the Application subclass for you, and then calls its start(...) method. So the instance for which you call all the setXXX(...) methods is not the instance whose start(...) method is invoked.
You're defining the reusable part in the wrong place: an Application subclass is inherently not reusable. You should regard the start() method in your Application subclass as the equivalent of the main() method in a regular JavaFX application.
So:
public class RushBrowser {
private final BorderPane view ;
private String url = "www.google.com";
private final String fullUrl = "http://" + url;
private String title = "Simple Browser";
private int height = 750;
private int width = 750;
private WebEngine engine ;
public RushBrowser() {
WebView browser = new WebView();
view = new BorderPane(browser);
engine = browser.getEngine();
}
public Node getView() {
return view ;
}
public void show(Stage stage) {
Scene scene = new Scene(view, width, height);
stage.setScene(scene);
stage.show();
}
public void show() {
show(new Stage());
}
public void setURL(String u) {
url = u;
}
public void setHeightWidth(int h, int w) {
height = h;
width = w;
view.setPrefSize(w, h);
}
public void setTitle(String t) {
title = t;
Scene scene = view.getScene();
if (scene != null) {
Window window = scene.getWindow();
if (window instanceof Stage) {
((Stage)window).setTitle(title);
}
}
}
}
and then you test it with an Application subclass:
public class RushBrowserTest extends Application {
#Override
public void start(Stage primaryStage) {
RushBrowser rush = new RushBrowser();
rush.setTitle("Test Title");
rush.setURL("www.github.com");
rush.setHeightWidth(1000, 1000);
rush.show(primaryStage);
}
public static void main(String[] args) { launch(args); }
}
and of course you can use it elsewhere. As an arbitrary example:
TabPane tabPane = ... ;
RushBrowser rush = new RushBrowser();
rush.setURL("www.github.com");
Tab tab = new Tab();
tab.setContent(rush.getView());

Resources