Search code examples
javajavafxfxmlscenebuilder

How to stop JavaFx Slider controls from resetting to their default values when switching between windows


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.


Solution

  • 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:

    1. 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();
            }   
        }   
    }
    
    1. Suggestion 2 is to store them somewhere, maybe in some handler class or in a file if you wish for them to be persistent.
    2. 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 :)