Please explain why the following is happening and what i need to change in order to make the FXML Button behave like the hardcoded one.
I am trying to change the CSS of an application on the fly by pressing a button. If i hardcode the button in my controller everything is working as expected, but when using FXML the same function call leads to a nullpointer.
Update to show full code (small test program anyways). pls Note i changed some names to make it easier to read. No functionality has been changed, but the added functionality that serves as a weak workaround meanwhile: Controller:
public class SoC extends Application
{
public Scene myScene;
Button btn = new Button();
@FXML
Button btnFXHack;
@FXML
Button btnFXFail;
boolean flipIt = false;
// basic function i want to use
public void changeCSS()
{
if (!flipIt)
{
myScene.getStylesheets().remove(0);
myScene.getStylesheets().add(getClass().getResource("styleTwo.css").toExternalForm());
}
else
{
myScene.getStylesheets().remove(0);
myScene.getStylesheets().add(getClass().getResource("styleOne.css").toExternalForm());
}
flipIt = !flipIt;
}
// workaround that is fine unless there are more then one scene
public void changeCSSHack(Scene s)
{
if (!flipIt)
{
s.getStylesheets().remove(0);
s.getStylesheets().add(getClass().getResource("styleTwo.css").toExternalForm());
}
else
{
s.getStylesheets().remove(0);
s.getStylesheets().add(getClass().getResource("styleOne.css").toExternalForm());
}
flipIt = !flipIt;
}
/**
* Button one changes CSS on his own scene only, which feels kinda hacked and lame.
*
* @param event
*/
@FXML
private void handleCSSHack(ActionEvent event)
{
// this button can access his own scene AND use it,....
changeCSSHack(btnFXHack.getScene());
}
/**
* Button two tries to change CSS with saved values in the mainController...
* ...and fails.
*
* @param event
*/
@FXML
private void handleCSSFail(ActionEvent event)
{
// ...yet this button tells me the SAME scene is null. Yes, the one he is on himself.
changeCSS();
}
@Override
public void start(Stage primaryStage)
{
try
{
BorderPane pane = (BorderPane) FXMLLoader.load(getClass().getResource("SoC.fxml"));
myScene = new Scene(pane);
myScene.getStylesheets().add(getClass().getResource("styleOne.css").toExternalForm());
btn = new Button("hardcoded");
btn.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
changeCSS();
}
});
pane.setCenter(btn);
primaryStage.setScene(myScene);
primaryStage.show();
System.out.println("Started");
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
launch(args);
}
}
Part of my FXML (board does not seem to like FXML, so can not paste in full)
<Button fx:id="btnFXHack" mnemonicParsing="false" onAction="#handleCSSHack" text="FX Button Hack" BorderPane.alignment="CENTER" />
<Button fx:id="btnFXFail" mnemonicParsing="false" onAction="#handleCSSFail" text="FX Button Fail" BorderPane.alignment="CENTER" />
As a side note: If i just add the FXML button to my pane via "children().add(btnFX)" i now have it twice BUT it is also working as well. Id really prefer to keep my GUI stuff out of my code tho, so id really love to hear how to fix this issue.
Thanks.
Edit to show my structure:
programmfolder
-src
--mainpackage
---controller.java
---.fxml-file
-themes folder
--default.css
--SciFi.css
So yes, the "/" is needed to reach the .css files. And again: changeCSS() is indeed working when being called by the hardcodes button. So i doubt the issue is on this end.
Also the FXML button can be pressed for println("stuff"). Right now i get the feeling he just does not see myScene and therefore gets the nullpointer. Even a simple system.out.println(myScene.toString()) leads to a nullpointer for the FXML button. The question is: Why is the same object suddenly null depending on where it is being called from?