0

I'm building a weather application. There are 2 scenes with 2 controller files. One is a main screen and the other one is for "settings". The main FXML contains a label, which must be turned on/off in the settings page, if the user does not want to see that extra bit of information. My question is how to setVisible that label from the controller class of the setting page, if it's possible at all. Thanks for your help

Laszlo Moricz
  • 11
  • 1
  • 3
  • What is the relationship between the two FXML files? (E.g. when do you show the settings page?) You should never expose nodes outside the controller, but either put functionality in the controller you can call to control the state of the UI, or (better) share a model between the two controllers and modify/observe data in it. You need to show more detail about how you have things set up for anyone (or, at least, for me: others may be more clairvoyant...) to be able to help. – James_D Feb 29 '16 at 22:36
  • James D is right. If you store change this visibility directly in the controller then it will be difficult to get the data from other sources (e.g. a settings file). You better store the settings in the model. – fabian Feb 29 '16 at 22:56

1 Answers1

4

I'm assuming the settings scene only comes up when the user clicks a button on the main screen. I have had to handle the same situation in my code recently. Here is a great tutorial that handles this situation:

http://code.makery.ch/library/javafx-2-tutorial/part1/

1.) In your MainScene Controller you will reference the main class and call its function to pop up the Settings Scene.

2.) In your main class you will have a function that pops up the Settings Scene

3.) After the Settings Scene is closed it will pass the value back to the MainScene Controller through the Main class and based on the returned value you can set the label.

1.) Your MainController for your Main scene will have a reference to the main class and a function to call the Settings Scene through the main class.

public class MainController {

@FXML
private Label label;
@FXML
private Button Settings;


// Reference to the main application
private MainApp mainApp;

/**
 * The constructor.
 * The constructor is called before the initialize() method.
 */
public MainController() {
}

/*Tie this function to your button that pops up Settings */

private void handleSettingsButton() { 

      /* Here you call a function in the main class and pass the label
       * to the settings scene controller */

        boolean show = mainApp.showSettingsScene(label);
        if (show) {
                label.isVisible("True");
         }
        else {
                label.isVisible("False");
        }
}

/**
 * Is called by the main application to give a reference back to itself.
 * 
 * @param mainApp
 */
public void setMainApp(MainApp mainApp) {
    this.mainApp = mainApp;


}
}

2.) In your main class (not to be confused with your main scene) you load the Main scene and call the setMainApp function to give your controller a reference back to the Main Class.

    public class MainApp extends Application {

private Stage primaryStage;
private BorderPane rootLayout;

@Override
public void start(Stage primaryStage) {
    this.primaryStage = primaryStage;
    this.primaryStage.setTitle("Main");

 /*Right when the app is loaded the MainScene shows up.*/

    try {
        // Load the root layout from the fxml file
        FXMLLoader loader = new FXMLLoader(MainApp.class.getResource("view/MainScene.fxml"));
 /* Get a reference to the controller instance of the main Scene */
mainSceneController = loader.getController();
    /*Allow the controller to talk to the main class */
    mainSceneController.setMainApp(this);
        rootLayout = (BorderPane) loader.load();
        Scene scene = new Scene(rootLayout);
        primaryStage.setScene(scene);
        primaryStage.show();
    } catch (IOException e) {
        // Exception gets thrown if the fxml file could not be loaded
        e.printStackTrace();
    }


}

/**
 * Returns the main stage.
 * @return
 */
public Stage getPrimaryStage() {
    return primaryStage;
}

/*This function referenced in your main controller will show the Settings
 *Scene and wait to see what the user has selected for the visible or not 
 *visible selection.  We need to pass the label to it as well, so we
 *accurately load the Settings Scene with the current state of the label
 */

public boolean showSettingsScene(Label label) {
    try {
      // Load the fxml file and create a new stage for the popup
    FXMLLoader loader = new FXMLLoader(MainApp.class.getResource("view/SettingsScene.fxml"));
settingsSceneController = loader.getController();

    /* Here we send the label to the controller instance of the Settings
     * Scene */

    controller.setLabel(label);
    AnchorPane page = (AnchorPane) loader.load();
    Stage dialogStage = new Stage();
    dialogStage.setTitle("Settings");
    dialogStage.initModality(Modality.WINDOW_MODAL);
    dialogStage.initOwner(primaryStage);
    Scene scene = new Scene(page);
    dialogStage.setScene(scene);

/* Show the dialog and wait until the user closes it*/
dialogStage.showAndWait();

 /*Return the value that the user has selected for visible or not */
return controller.isShowOrHide();

   } catch (IOException e) {
// Exception gets thrown if the fxml file could not be loaded
e.printStackTrace();
     return false;
   }
 }

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

3.) Your Settings Scene Controller will look something like the following:

import...
public class SettingsSceneController{

@FXML  private ComboBox showOrHide;

private Stage dialogStage;
private Boolean show = false;
private Label label;

/**
 * Initializes the controller class. This method is automatically called
 * after the fxml file has been loaded.  
 */
@FXML
private void initialize() {
     ;I don't know what you have, but if you use a Combobox...
     showOrHide.getItems().addAll(
          "Show",
          "Hide",);

}

/**
 * Sets the stage of this dialog.
 * @param dialogStage
 */
public void setDialogStage(Stage dialogStage) {
    this.dialogStage = dialogStage;
}

/*The label that was passed from Main Scene Controller to Main Class to 
 * here is now used in the function to update the Combobox with the
 * current status of the label */
public void setLabel(Label label) {
    this.label = label;
     if(label.isVisible){
          showOrHide.setValue("Show");
          show = true;
     }
     else{
          showOrHide.setValue("Hide"); 
          show = false;
     }
}



/**
 * Returns true if the user clicked OK, false otherwise.
 * @return
 */
public boolean isShowOrHide() {
    return show;
}

/**
 * Called when the user clicks ok. Attach this in Scene Builder,to the OK,
 * Enter or Apply or whatever you called it button of the Settings Scene 
 * It will reflect any change made to the combobox.
 */
@FXML
private void handleOk() {
    if (showOrHide.getValue().toString() == "Show") {
        show= true;
     }
    else{
        show = false;
     }
        dialogStage.close();

}

/**
 * Called when the user clicks cancel if you have a cancel button.
 */
@FXML
private void handleCancel() {
    dialogStage.close();
}


}
}

I took most of this code from the tutorial and custom tailored it to your solution. It is kind of bouncing around to three classes, but if you think on it a little bit you can see how they are communicating between the controllers using the main class to facilitate it. I did not test this, but it should be pretty close to what you need.

halfer
  • 19,824
  • 17
  • 99
  • 186
Brian Stallter
  • 159
  • 1
  • 1
  • 16