I'm still new in Java, and I have a problem with passing objects between two windows in JavaFX.
I have mainStage with mainStageController. There are two things inside the controller: a label and a button. The button is opening a new window (childStage) with childStageController.
In childStageController I have a method, which gets the text "TEST CONTENT" from textField. I want to pass this text to the existing mainStage and set the label text to "TEST CONTENT".
There are a lot of topics handle similar problems, but in every case I found, the problem is with passing the object to a new created window. My case is exactly the opposite: I need to pass the object from the new opened window to the existing one.
#FXML
private Label label;
#FXML
private Button button;
private void configureButton() {
button.setOnAction(event -> {
newStageOpener(mainPaneResource, mainPaneStageTitle);
// There is magic to do
label.setText(message);
});
}
private void newStageOpener(String resource, String stageTitle){
try {
Parent parent = FXMLLoader.load(getClass().getResource(resource));
Scene scene = new Scene(parent);
Stage stage = new Stage();
stage.setScene(scene);
stage.setTitle(stageTitle);
stage.showAndWait();
} catch (IOException e) {
e.printStackTrace();
}
}
I try to do something like this, but every time I get a NullPointerException (the debugger says that childStageController is null).
FXMLLoader loader = new FXMLLoader(getClass().getResource(childPaneResource));
ChildStageController childStageController = loader.getController();
String message = childStageController.getMessage;
I also try: using setLocation, using loader.load(), "reverse" problem (setText directly from childStageController, instead of getting it from mainStageController), put newStageOpener into a configureButton, but nothing works - I always get the same NullPointerException.
What should I do to fix it?
There was mistake in newStageOpener - I rebuild it like this:
private void newStageOpener(String resource, String stageTitle) {
try {
FXMLLoader loader = new FXMLLoader(ChildStageController.class.getResource(resource));
Parent root = loader.load();
Scene scene = new Scene(root);
Stage stage = new Stage();
stage.setScene(scene);
stage.setTitle(stageTitle);
stage.showAndWait();
getObjectFromResource(resource, loader);
} catch (IOException e) {
e.printStackTrace();
}
}
private void getObjectFromResource(String resource, FXMLLoader loader) {
loader.setLocation(ChildStageController.class.getResource(resource));
ChildStageController childStageController = loader.getController();
message = childStageController.getMessage();
}
getMessage obviously is getting text from TextField.
Now it's working perfect.
Related
I have a bunch of fxml windows that I built in SceneBuilder for javafx. When I switch between my settings window and my main menu the sliders (for various sound levels) reset to their original value, however i would want them to keep the values they were at last time the window was open.
I've tried settings values in initialise and settings a variable through the class that is changed and used to set the sliders when it is launched, but that didn't work either.
The FXML
<!-- The slider code (scenebuilder forces me to choose a value) ->
<Slider fx:id="soundfxSlider" onMouseDragged="#updateSFX" showTickLabels="true" showTickMarks="true" snapToTicks="true" value="50.0" />
<Slider fx:id="musicSlider" minorTickCount="1" onMouseDragged="#updateMusic" showTickLabels="true" showTickMarks="true" snapToTicks="true" />
The method for launching accessed by the main menu and other windows when switching
static void launchScreen(String fileName) {
fileName = "/screens/" + fileName + ".fxml";
try {
Parent root = FXMLLoader.load(MainMenuController.class.getResource(fileName));
Scene scene = new Scene(root);
stage.setTitle("Fortress Combat");
stage.setScene(scene);
stage.show();
stage.setOnCloseRequest(t -> {
Platform.exit();
System.exit(420);
});
} catch (IOException e) {
e.printStackTrace();
}
}
The settings controller
public class SettingsController {
#FXML // fx:id="soundfxSlider"
private Slider soundfxSlider; // Value injected by FXMLLoader
#FXML // fx:id="musicSlider"
private Slider musicSlider; // Value injected by FXMLLoader
#FXML
void updateMusic(MouseEvent event) {
double musicVolume = musicSlider.getValue();
//setMusicSlider(musicVolume);
Launcher.adjustVolume(musicVolume);
}
#FXML
void updateSFX(MouseEvent event) {
double vol = soundfxSlider.getValue();
//setSoundfxSlider(vol);
Launcher.adjustSfx(vol);
}
private void setMusicSlider(double sliderVal) {
musicSlider.setValue(sliderVal);
}
private void setSoundfxSlider(double sfxVal) {
soundfxSlider.setValue(sfxVal);
}
#FXML
void playTestSfx(ActionEvent event) {
Launcher.playTestSFX();
}
#FXML
void goBack(ActionEvent event) {
Launcher.launchScreen("main_menu");
}
#FXML // This method is called by the FXMLLoader when initialization is complete
void initialize() {
assert soundfxSlider != null : "fx:id=\"soundfxSlider\" was not injected: check your FXML file 'settings.fxml'.";
assert musicSlider != null : "fx:id=\"musicSlider\" was not injected: check your FXML file 'settings.fxml'.";
}
If no value is given to the sliders in the fxml they take the default of 0 - it seems that a default value is forced and it doesn't "remember" when windows are switched.
I believe I understand why you get the default values on your slider all the time that you open up a new window or rather loading a new window.
If you take a look at the code below we see that you create a new parent by loading your FXML with the name that is given as an argument, after which you set the new Scene to your stage. The key thing to note from this is that you create a new parent and scene which has no idea about any values set you your sliders in other scenes.
static void launchScreen(String fileName) {
fileName = "/screens/" + fileName + ".fxml";
try {
Parent root =
FXMLLoader.load(MainMenuController.class.getResource(fileName));
Scene scene = new Scene(root);
stage.setTitle("Fortress Combat");
stage.setScene(scene);
stage.show();
stage.setOnCloseRequest(t -> {
Platform.exit();
System.exit(420);
});
} catch (IOException e) {
e.printStackTrace();
}
}
A few suggestions:
Save the actual scene and then if the filename argument corresponds to an already created scene then you can really "switch" scene instead of creating a new one.
Below is a quickly made example with an hard coded if statement which is just to show you what I mean, but I'm sure you could solve it better than that and with less duplicated code.
private Scene sceneA;
static void launchScreen(String fileName) {
if(fileName.equals("sceneA") && sceneA != null){
/*if we want to open up sceneA and it has been created before (meaning it's not null) then open the already existing scene.*/
stage.setTitle("Fortress Combat");
stage.setScene(sceneA);
stage.show();
stage.setOnCloseRequest(t -> {
Platform.exit();
System.exit(420);
});
}else{
fileName = "/screens/" + fileName + ".fxml";
try {
Parent root =
FXMLLoader.load(MainMenuController.class.getResource(fileName));
Scene scene = new Scene(root);
stage.setTitle("Fortress Combat");
stage.setScene(scene);
stage.show();
stage.setOnCloseRequest(t -> {
Platform.exit();
System.exit(420);
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
Suggestion 2 is to store them somewhere, maybe in some handler class or in a file if you wish for them to be persistent.
or suggestion 3 which could be to maybe declare some static variables in your controller which you set to the values of your sliders when the slider values have changed. Then you may set the slider values in your initialize method by accessing your static variables.
Anyway, those were my thoughts and I hope that I could be of some help.
Let us know how it works out :)
Hi guys a little programmer java Swing and I'm trying a new technology like JavaFx just that I just can not figure out how to make operations for navigation between views, in particular.
How can I replace a main view on the stage? for example I have a view that I associate with the calback start method in the scene in the following way
#Override
public void start(Stage primaryStage) {
vistaPrincipale.load();
Scene scene = new Scene(vistaPrincipale.getRoot());
primaryStage.setScene(scene);
primaryStage.show();
}
and during the execution of the program I would like to change the view one with the view two, only that I could not really understand how I did not even find enough material to solve the problem, in swing it was enough to change the frame content bread
another problem that I have encountered is to launch a secondary panel like a jdialog in swing, I solved this problem by creating a new stage and using it in the following way, but to be honest it really seems like a very bad solution
public class InfoAutori {
private static final Logger LOGGER = LoggerFactory.getLogger(InfoAutori.class);
private Stage stage;
public void init(){
FXMLLoader load = new FXMLLoader();
Parent root = new AnchorPane();
try {
root = load.load(getClass().getResourceAsStream("InfoAutori.fxml"));
} catch (Exception e) {
LOGGER.error("Si e' verificato un errore del tipo: " +
e.getLocalizedMessage());
e.printStackTrace();
}
stage = new Stage();
Scene scene = new Scene(root);
stage.setScene(scene);
}
public void visualizza(){
stage.showAndWait();
}
}
I am very new to using JavaFX and have having some trouble using JavaFX with FXML. I am creating a program with a "Setup" button that when clicked, opens a new window with the connection (to an Arduino) settings. On the menu is another button ("Connect") that connects to the board. This closes the window. I'm looking to have this change the text of the original "setup" button to "disconnect", however, I don't seem to be able to access the button from the "setup window". Whenever I click "connect" I get the following error:
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
I read online that this is a wrapper for a null pointer exception. I assume that the "setup" button is null and that's why I can't change it, but I can't work out why.
Here is an excerpt from MainController.java:
#FXML
protected void setUpConnection(ActionEvent e) {
SetupController setupController = new SetupController();
setupController.init(this);
}
The above method gets called when the "setup" button is clicked (set in the file: setupMenu.fxml). This then opens up the separate window. Here is the code in SetupController.java that opens the window:
private void openSetupWindow() {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("setupMenu.fxml"));
Parent root1 = (Parent)loader.load();
Stage stage = new Stage();
stage.setTitle("Setup Connection");
stage.setScene(new Scene(root1));
stage.show();
} catch(Exception exc) {
exc.printStackTrace();
}
}
When the connect button is clicked, the following method (in SetupController.java) is called:
private void changeButtonText(ConnectionEventType e) {
Button b = main.getSetupButton();
if(e == ConnectionEventType.CONNECT) {
b.setText("Disconnect");
}
else {
b.setText("Setup Connection...");
}
}
(main is the MainController object that was passed in to setupController.init() )
The above code is where I am getting the error.
Just to clarify, I have 2 separate fxml files, one for the main window and one for the pop up. sample.fxml(the main window) has its controller set to MainController and is set up in Main.java (below):
#Override
public void start(Stage primaryStage) throws Exception{
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
GridPane root = loader.load();
Scene scene = new Scene(root, 1200, 900);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setTitle("Nest Control");
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
Am I trying to access the button incorrectly? Is anyone able to help? Like I said, I don't have much experience with using JavaFX or FXML.
I think the answer you are looking for is to store the controllers for each window you open so you can access the variables within the controllers, however without the rest of your code would be hard to advise you, but heres an example of what i mean:
private SetupController yourController;
#Override
public void start(Stage primaryStage) throws Exception{
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
GridPane root = loader.load();
this.yourController=loader.<SetupController>getController();
Scene scene = new Scene(root, 1200, 900);
}
}
You could then pass the variable yourController to other instances in a Model-view-controller type way and access its methods.
or something like this in your case :
private void changeButtonText(ConnectionEventType e) {
Button b = this.yourController.getButton(); //a method that returns your #FXML button object in your controller
if(e == ConnectionEventType.CONNECT) {
b.setText("Disconnect");
}
else {
b.setText("Setup Connection...");
}
}
Or alternatively have a specific method within the controller that will set the text of the button without having to return the button object.
See the examples here and here
However please note the error you get seems to typically attributed to missing #FXML annotations so maybe make sure in this instance that you have annotated all the variables in any controllers also. See here for more details.
So, I have two .fxml files, the other one working as main and the other one as basically window that pops up.
#FXML private void handleSelection() throws Error {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("StudentChoose.fxml"));
Parent root1 = (Parent) fxmlLoader.load();
Stage stage = new Stage(StageStyle.DECORATED);
stage.setScene(new Scene(root1));
stage.show();
} catch(Exception e) {
e.printStackTrace();
}
sBox.setItems(FXCollections.observableArrayList("New Document", "Open ", "Save", "Save as"));
}
This is what I have. I have stated the sBox above with
#FXML private ChoiceBox sBox;
Everything works PERFECTLY when the choicebox/combobox is on the main stage, but when it is on the other stage - it doesn't. I have a .fxml file for this another stage with the content, id is set as 'sBox' for the choicebox - but it doesn't work. Instead it returns me a java.lang.NullPointerException on the line where I call to add items to it. What could be the problem?
So I am working on building an application that is kind of like an IDE, think Eclipse or Netbeans. I want to have different zones where users can select something like a view that will show up in one of the zones. My first attempt is to add a console view to on of my zones. In my application class I create the overall UI with the following code:
public void initialize() {
try {
mainController = (MainController) replaceSceneContent("/layout/main.fxml");
}catch (Exception ex) {
Logger.getLogger(MyApplication.class.getName()).log(Level.SEVERE, null, ex);
}
}
private Initializable replaceSceneContent(String fxml) throws Exception {
FXMLLoader loader = new FXMLLoader();
InputStream in = MyApplication.class.getResourceAsStream(fxml);
loader.setBuilderFactory(new JavaFXBuilderFactory());
loader.setLocation(MyApplication.class.getResource(fxml));
AnchorPane page;
try {
page = (AnchorPane) loader.load(in);
} finally {
in.close();
}
Scene scene = new Scene(page);
stage.setScene(scene);
stage.sizeToScene();
return (Initializable) loader.getController();
}
Which results in the following UI
I would then like to add a console widget/view to the southEast quadrant of the application. This will be handled by the MainController. In theory if someone chooses the Console view it will popup in one of the quadrants, but for now I just want to load it at start up. I try to do this with the following code:
public class MainController extends AnchorPane implements Initializable{
#FXML
SplitPane west, east;
#FXML
AnchorPane northWest, southWest, northEast, southEast, applicationHeader;
#FXML
MenuBar menuBar;
Stage stage;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
try {
ConsoleWidget cw = (ConsoleWidget)loadWidget("/widget/console.fxml");
southEast.getChildren().add(cw);
} catch (Exception e) {
e.printStackTrace();
}
}
private Initializable loadWidget(String fxml) throws Exception {
FXMLLoader loader = new FXMLLoader();
InputStream in = MainController.class.getResourceAsStream(fxml);
loader.setBuilderFactory(new JavaFXBuilderFactory());
loader.setLocation(MainController.class.getResource(fxml));
AnchorPane page;
try {
page = (AnchorPane) loader.load(in);
} finally {
in.close();
}
return (Initializable) loader.getController();
}
}
This should result in the following console appearing in the south east quadrant.
Programatically this seems to work. My southEast anchor pane shows that it has 1 child in its list if you look at it, however my UI doesnt change. I need to know how to refresh my Main controller (or do whatever I need to do) to make the change show up to the user.
I have created applications before where I create an entire new scene as the user goes through different pages but I have never really done something like this where I need to update the scene after adding a node to a pane. I have tried things like stage.sizeToScene() to try to refresh it but that hasnt worked for me.
You must return AnchorPane page instead of (Initializable) loader.getController(); to be added to the southEast anchorPane.
Declaring a Controller which extends another Node, results in a controller-node, which are used for creating Custom Controls.
These controls have constructors to set the Controller and the Root node. See this example
In your case, loader.getController() returns you an AnchorPane(rather ConsoleWidget), which is not loaded from the FXML, but is an instance of the ConsoleWidget class.