I have a singleton called MenuText
. It is responsible for displaying the correct text in the menu. It gets updated dynamically.
public class MenuText implements LanguageObserver {
private MenuText() { //I want this to be private, only one instance should exist
Config.getInstance().subscribe(this);
}
private static class MenuTextHolder {
private static final MenuText INSTANCE = new MenuText();
}
public static MenuText getInstance() {
return MenuTextHolder.INSTANCE;
}
@Override
public void update(Language language) {
System.out.println("Updating...");
switch (language) {
case ENGLISH -> {
setText("Play");
}
case GERMAN -> {
setText("Spielen");
}
}
}
private final StringProperty text = new SimpleStringProperty("Play");
public StringProperty textProperty() {
return text;
}
public String getText() {
return text.get();
}
private void setText(String text) {
this.text.set(text);
}
}
I have a fxml file, but the MenuText can't have a reference to it. (This would contradict the MVVM architectural style)
<?import tiles.text.MenuText?>
<VBox alignment="TOP_CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
prefHeight="1080.0" prefWidth="1920.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="tiles.viewModel.GameMenuViewModel">
<!--here I want to bind to the text property-->
<Button text="???"/>
</VBox>
Initially I used <fx:define>
to setup a reference to the MenuText from the fxml file, but this doesn't allow private constructors. It shouldn't be that difficult, because MenuText is static, but I'm unable to make a static reference to it's singleton.
I tried <Button text="${MenuText.getInstance().text}">
Update
As mentioned in this answer, I shouldn't use the Singleton Pattern. Based on this I added an ApplicationFactory:
//Creation of items with application lifetime
public class ApplicationFactory {
private Config config;
public void build() {
config = new Config();
}
public Config getConfig() {
return config;
}
}
Is this the correct approach? I now have a MenuFactory, which gets also created in the JavaFX start()
method. It sets the parent of the scene.
public class MenuFactory {
public Parent getMenu(Config config, String fxmlLocation) {
MenuText menuText = new MenuText(config);
MenuViewModel menuViewModel = new MenuViewModel(config);
try {
FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(getClass().getResource(fxmlLocation)));
loader.getNamespace().put("menuText", menuText);
return loader.load();
} catch (IOException e) {
//...
}
}
}
The start() mehtod looks like this:
@Override
public void start(Stage primaryStage) {
ApplicationFactory applicationFactory = new ApplicationFactory();
applicationFactory.build();
MenuFactory menuFactory = new MenuFactory();
Parent root = menuFactory.getMenu(applicationFactory.getConfig(), MENU);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
This makes it way more complicated and I'm not sure if this is correct. Furthermore, I still don't know how I set the MenuText in the fxml file. I tried, but I think this isn't the correct way to set a namespace in fxml.
<fx:define>
<MenuText fx:id="menuText"/>
</fx:define>
I read these documentations but don't understand how I can set this custom namespace.