all the questions I found around here answered how to send data from one controller to another. However, I don't know how to return another value from the second controller to the first one. Basically, send data back and forth while the first controller is still opened.
Main class:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("Screen1.fxml"));
Scene scene = new Scene(loader.load());
primaryStage.setScene(scene);
primaryStage.setTitle("Screen 1");
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Screen 1 controller:
public class Screen1Controller {
#FXML
TextField textFieldScreen1;
#FXML
Button buttonScreen1;
#FXML
Label labelScreen1;
#FXML
private void initialize() {
buttonScreen1.setOnAction((event) -> {
try{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("Screen2.fxml"));
Scene scene = new Scene(loader.load());
Stage stage = new Stage();
//Send data to Screen 2
Screen2Controller controller2 = loader.getController();
controller2.receiveTextFromScreen1(textFieldScreen1.getText());
stage.setTitle("Screen 2");
stage.setScene(scene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
Screen 2 controller:
public class Screen2Controller {
#FXML
TextField textFieldScreen2;
#FXML
Button buttonScreen2;
#FXML
Label labelScreen2;
#FXML
private void initialize() {
buttonScreen2.setOnAction((event) -> {
Stage currentStage = (Stage) buttonScreen2.getScene().getWindow();
currentStage.close();
});
}
public void receiveTextFromScreen1(String stringScreen1){
labelScreen2.setText(stringScreen1);
}
}
I have tried to use the same strategy (create a method in Screen1Controller and instantiate Screen1Controller in the second controller) with the following code but it didn't work.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("Screen2.fxml"));
//Send data to Screen 1
Screen1Controller controller1 = loader.getController();
controller1.receiveTextFromScreen2(textFieldScreen2.getText());
Also, related to this matter (I'm assuming) a second question: how would I update the second view while I change the TextField in controller 1 without creating a new window everytime I click the button (i.e using the same second view instance)?
Any help will be greatly appreciated!
Create a constructor
public Screen1Controller {
Arrow the parameters
}
and when you call the controller, you parse the parameter:
void on2players(ActionEvent event) throws IOException {
client.out.println("ACTION NEWGAME 2 4");
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/boardscreen.fxml"));
loader.setController(new Screen1Controller(parameter);
Parent root = loader.load();
Scene scene= new Scene(root);
Stage window = (Stage) ((Node) event.getSource()).getScene().getWindow();
window.setScene(scene);
window.show();
}
public class Screen1Controller implements Initializable {
public Screen1Controller() {
//parameters
}
#Override
public void initialize(URL location, ResourceBundle resources) {
switch(numberOfPlayers){
case 2:
//TODO...creating board for 2 players
case 3:
//TODO creatinf board for 3 players
}
}
If you do this, you force the fxml loader to call the controller. NOTE: You should get the controller inside .FXML
Related
User clicks on a button that opens a popup second scene that allows the user to select some values, then close and pass the selection to the first scene.
First Controller
Set<String> set;
public void initialize(URL url, ResourceBundle rb){
set = new TreeSet<String>():
}
#FXML
public Set<String> addValue (MouseEvent e) throws IOException {
Stage stage = new Stage ();
root = FXMLoader.load(getClass).getResources(2ndFXML.fxml);
stage.initModality(Modality.APPLICATION_MODAL);
stage.iniOwner(clickedButton.getScene().getWindow();
stage.showAndWait():
return set;
}
2nd Controller
#FXML
public void addSelection (MouseEvent e) throws IOException {
if (event.getSource() == button){
stage = (Stage) button.getScene().getWindow();
set.addAll(listSelection)
stage.close
}
But the value never makes it back to the first controller.
Since you're using showAndWait(), all you need to do is define an accessor method for the data in the second controller:
public class SecondController {
private final Set<String> selectedData = new TreeSet<>();
public Set<String> getSelectedData() {
return selectedData ;
}
#FXML
private void addSelection (MouseEvent e) {
// it almost never makes sense to define an event handler on a button, btw
// and it rarely makes sense to test the source of the event
if (event.getSource() == button){
stage = (Stage) button.getScene().getWindow();
selectedData.addAll(listSelection)
stage.close();
}
}
}
Then retrieve it in the first controller when the window has been closed:
#FXML
public void addValue(MouseEvent e) throws IOException {
Stage stage = new Stage ();
FXMLLoader loader = new FXMLLoader(getClass().getResource(2ndFXML.fxml));
Parent root = loader.load();
// I guess you forgot this line????
stage.setScene(new Scene(root));
stage.initModality(Modality.APPLICATION_MODAL);
stage.iniOwner(clickedButton.getScene().getWindow();
stage.showAndWait();
SecondController secondController = loader.getController();
Set<String> selectedData = secondController.getSelectedData();
// do whatever you need to do with the data...
}
I am fairly new to JAVAFX so please bear with me. Any help is greatly appreciated. Firstly, i have a Main app that loads addItems.fxml and addItemsController.
public class UncookedApp extends Application {
private Stage stage;
#Override
public void start(Stage primaryStage) {
stage = primaryStage;
try {
FXMLLoader loader=new FXMLLoader();
loader.setLocation(getClass().getResource("/uncooked/view/addItems.fxml"));
AnchorPane root=loader.load();
addItemsController itemsCtrl=loader.getController();
itemsCtrl.setMainApp(this);
Scene scene=new Scene(root,1024,768);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e){
e.printStackTrace();
}
}
public Stage getStage() {
return stage;
}
public static void main(String[] args) {
launch(args);
}
}
This is part of the addItemsController:
private UncookedApp mainApp;
public UncookedApp getMainApp() {
return mainApp;
}
public void setMainApp(UncookedApp mainApp) {
this.mainApp = mainApp;
}
public void toMeat(ActionEvent event) {
Stage stage1=mainApp.getStage();
try {
FXMLLoader loader=new FXMLLoader();
loader.setLocation(getClass().getResource("/uncooked/view/addMeat.fxml"));
Parent root=loader.load();
addMeatCtrl itemsCtrl=loader.getController();
// itemsCtrl.setMainApp(this);
Scene scene=new Scene(root,1024,768);
stage1.setScene(scene);
stage1.show();
} catch(Exception e){
e.printStackTrace();
}
}
The addItemsController will load a different page, addMeat. I got that to work but when I try to go back from addMeat to addItems with a button handle, it doesn't work. Is it something to do with retrieving the mainApp's stage? How can I solve this?
This is what I've tried.
public void handleBackFromMeat(ActionEvent event) {
try{
Stage stage=mainApp.getStage();
FXMLLoader loader=new FXMLLoader();
loader.setLocation(getClass().getResource("/uncooked/view/addItems.fxml"));
Parent root=loader.load();
addItemsController itemsCtrl=loader.getController();
Scene scene=new Scene(root,1024,768);
stage.setScene(scene);
stage.show();
}catch (Exception e){
e.printStackTrace();
}
}
I also had posted a similar question and then i went ahead with my way
I need to create a back button functionality in javafx?
What you can do as i did for my application was make two static list at the start of the application and then fill it with the Stage and the Fxml page name if you are using fxml and then have a central back button method on each page to move the previous page by iterating on the last item added to the list .
Two static lists in the main class
public static List<Stage>stageval = new ArrayList<Stage>();
public static List<String>fxmlval = new ArrayList<String>();
When ever i switch my fxml view i added the stage and the fxml file name to their respective lists.
fxmlval.add("Instance.fxml");
stage = (Stage)validate.getScene().getWindow();
stageval.add((Stage) delete.getScene().getWindow());
And a central back button method when ever the user clicked the back button i iterated to last item of both the lists and then after changing the stage deleted the values from the list .
public class BackButton {
public void back(String instance,Stage stage) throws IOException{
Parent parent = FXMLLoader.load(getClass().getResource(instance));
Scene scene = new Scene(parent);
scene.getStylesheets().add("/css/Style.css");
stage.setResizable(false);
stage.setScene(scene);
stage.show();
}
}
#FXML
public void back() throws IOException {
BackButton bb = new BackButton();
bb.back(fxmlval.get(fxmlval.size() - 1),
stageval.get(stageval.size() - 1));
fxmlval.remove(fxmlval.size() - 1);
stageval.remove(stageval.size() - 1);
}
Hope this helps its a work around but the solution works.
I'm using JavaFX and working on a Schoolproject right now.
About my program, i have a login screen and if i press on login, i come to the mainframe. The content area of the mainframe is empty, because i load the content for the AnchorPane that builds the mainframe when i use the buttons in the menubar.
When i press the login button, i want to laod the mainframe and the home pane in it. But i don't know how to load the pane into the mainframe from another class.
Here my code:
Code to load the mainframe:
#FXML
protected void login(ActionEvent event) throws IOException, URISyntaxException {
Stage stage;
Parent root = null;
stage = (Stage) btnLogin.getScene().getWindow();
try{
root = FXMLLoader.load(getClass().getResource("view/mainframe.fxml"));
} catch (IOException e){
e.printStackTrace();
}
Scene scene = new Scene(root, 1280, 900);
stage.setScene(scene);
stage.show();
}
Code to laod the home pane into the mainframe:
#FXML
protected void mHome(ActionEvent event) throws IOException, URISyntaxException {
try {
URL url = getClass().getResource("view/home.fxml");
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(url);
fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
AnchorPane page = (AnchorPane) fxmlLoader.load(url.openStream());
aContent.getChildren().clear();
aContent.getChildren().add(page);
}
catch (IOException e) {
e.printStackTrace();
}
}
These two methods are in different classes.
If you need some more information, please let me know :)
Thank you and greets, Timo
Just invoke the mHome() method. Note that, since you never use the ActionEvent, you can omit it from the method signature (the FXMLLoader will still be able to map it as an event handler). So change it to
#FXML
protected void mHome() throws IOException, URISyntaxException {
try {
URL url = getClass().getResource("view/home.fxml");
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(url);
fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
AnchorPane page = (AnchorPane) fxmlLoader.load(url.openStream());
aContent.getChildren().clear();
aContent.getChildren().add(page);
}
catch (IOException e) {
e.printStackTrace();
}
}
If you always want to show the "home pane" when the main pane is first shown, just add a call to mHome to the initialize() method in the same controller class:
public void initialize() {
// existing code...
mHome();
}
Alternatively, if you specifically only want to show the "home pane" when you load it from the login pane, add a call to mHome in your login handler:
#FXML
protected void login(ActionEvent event) throws IOException, URISyntaxException {
Stage stage;
Parent root = null;
stage = (Stage) btnLogin.getScene().getWindow();
try{
FXMLLoader loader = new FXMLLoader(getClass().getResource("view/mainframe.fxml"));
root = loader.load();
MainController controller = loader.getController();
controller.mHome();
} catch (Exception e){
e.printStackTrace();
}
Scene scene = new Scene(root, 1280, 900);
stage.setScene(scene);
stage.show();
}
(I'm assuming MainController is the name of the controller class for mainframe.fxml; obviously just adjust as needed.)
I have decided to update my application using JavaFXML, However I am having difficulties passing a scene into my controller. Here is my Controller;
public class MainApp extends Application {
#FXML
public Stage primaryStage;
#FXML
private AnchorPane rootLayout;
#FXML
private JobInterface jInterface;
#Override
public void start(Stage primaryStage) {
primaryStage = new Stage();
setPrimaryStage(primaryStage);
initRootLayout();
}
#FXML
public void initRootLayout(){
try {
primaryStage = getPrimaryStage();
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("MainInterface.fxml"));
rootLayout = (AnchorPane) loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
setPrimaryStage(primaryStage);
} catch (IOException e) {
e.printStackTrace();
}
}
#FXML
private void setJobLayout(){
primaryStage = getPrimaryStage();
jInterface = new JobInterface();
jInterface.initJobLayout();
primaryStage.setScene(jInterface.getScene());
}
public static void main(String[] args) {
launch(args);
}
public Stage getPrimaryStage() {
return primaryStage;
}
public void setPrimaryStage(Stage primaryStage) {
this.primaryStage = primaryStage;
}
}
Here is a method that is changing the scene using a different FXML file and attempting to pass the scene back to the controller;
public class JobInterface {
private AnchorPane rootLayout;
private Scene scene;
public void initJobLayout(){
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("JobInterface.fxml"));
rootLayout = (AnchorPane) loader.load();
scene = new Scene(rootLayout);
setScene(scene);
} catch (IOException e) {
e.printStackTrace();
}
}
public Scene getScene() {
return scene;
}
public void setScene(Scene scene) {
this.scene = scene;
}
}
The issue I'm having now is a NullPointerException on this line in the main app;
primaryStage.setScene(jInterface.getScene());
I am trying to pass a Stage between methods so that I can only update the Scene and not have to open a new Stage everytime a new method is called. Any ideas on where I am going wrong?
There should be no need to pass the stage or scene. Your Main will load the fxml resource which will have your fxml controller 'in charge' of your fxml file.
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("jobController.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
Your controller might look something like this (depending on your fxml design):
public class JobController {
#FXML
private Label label;
#FXML
private void handleButtonAction(ActionEvent event) {
label.setText("This is the controller speaking");
}
}
Now you can 'control' your stage (scene) from the controller. If you are going to create another class which also has to update the scene, pass a reference to the controller to it e.g. from the controller:
TimeClock timeClock = new TimeClock();
timeClock.init(this);
and then in TimeClock.java you have:
private final JobController controller;
public void init (JobController control){
this.controller = control;
}
Now you can access any public method in your controller from the TimeClock class. E.g.
controller.updateLabel("Time clock speaking!");
Working with SceneBuilder. I have 2 stages, each one with a controller: stage1Controller,stage2Controller.
Stage1Controller :
public class Stage1Controller {
#FXML
private MenuItem translate;
#FXML
private Menu file;
#FXML
private Menu edit;
#FXML
private Menu help;
#FXML
private void handleTranslate (ActionEvent event){
translateFirstStage();
//HOW TO ACCESS THE stage2Controller setLabel()??
}
private void translateFirstStage(){
file.setText("Fichier");
edit.setText("Modifier");
help.setText("Aide");
}
}
Stage2Controller:
public class Stage2Controller {
#FXML
private Label lb;
private void setLabel(String string){
lb.setText("string");
}
}
Here is how both fxml files are loaded in Main.java class using 2 methods (called in Start(Stage primaryStage) method):
public void firstStage() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/stage1.fxml"));
rootLayout = (BorderPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public void secondStage() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/stage2.fxml"));
XD = (AnchorPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(XD);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
The handleTranslate(ActionEvent event) method is used as an OnAction method for the MenuItem translate in the first Stage, it translates the view in both stages.
How Can i put setLabel in handleTranslate Method ? Thanks
The "quick and dirty" way is to give the Stage1Controller a reference to the Stage2Controller:
public class Stage1Controller {
private final Stage2Controller stage2Controller ;
public void setStage2Controller(Stage2Controller stage2Controller) {
this.stage2Controller = stage2Controller ;
}
// ...
#FXML
private void handleTranslate (ActionEvent event){
translateFirstStage();
stage2Controller.setLabel(...);
}
// other code as before ...
}
Now in your main app:
public class MainApp extends Application {
private Stage1Controller stage1Controller ;
private Stage2Controller stage2Controller ;
#Override
public void start(Stage primaryStage) {
firstStage();
secondStage();
stage1Controller.setStage2Controller(stage2Controller);
// ...
}
public void firstStage() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/stage1.fxml"));
rootLayout = (BorderPane) loader.load();
stage1Controller = loader.getController();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public void secondStage() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/stage2.fxml"));
XD = (AnchorPane) loader.load();
stage2Controller = loader.getController();
// Show the scene containing the root layout.
Scene scene = new Scene(XD);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
// ...
}
A more robust approach to this, though, is to let both controllers access a shared model class, storing the data. If you represent the data using JavaFX observable properties, the controllers can listen for changes on the properties they care about. For example:
public class Model {
private final StringProperty text = new SimpleStringProperty("Initial text...");
public StringProperty textProperty() {
return text ;
}
public final void setText(String text) {
textProperty().set(text);
}
public final String getText() {
return textProperty().get();
}
// other properties as needed...
}
Now your controllers will look like this:
public class Stage1Controller {
private Model model ;
public void setModel(Model model) {
this.model = model ;
}
#FXML
private void handleTranslate (ActionEvent event){
translateFirstStage();
model.setText(...); // value will appear in stage2 controller's label!
}
// ...
}
and
public class Stage2Controller {
#FXML
private Label lb ;
private Model model ;
public void setModel(Model model) {
lb.textProperty().unbind();
this.model = model ;
lb.textProperty().bind(model.textProperty());
}
// ...
}
And in this case your main app looks like:
public class MainApp extends Application {
private final Model = new Model();
#Override
public void start(Stage primaryStage) {
// ...
}
public void firstStage() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/stage1.fxml"));
rootLayout = (BorderPane) loader.load();
Stage1Controller controller = loader.getController();
controller.setModel(model);
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public void secondStage() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/stage2.fxml"));
XD = (AnchorPane) loader.load();
Stage2Controller controller = loader.getController();
controller.setModel(model);
// Show the scene containing the root layout.
Scene scene = new Scene(XD);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}