将 StringProperty 绑定到单例中的标签
Bind StringProperty to Label from a Singleton
我有一个叫MenuText
的单身人士。它负责在菜单中显示正确的文本。它会动态更新。
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);
}
}
我有一个 fxml 文件,但 MenuText 无法引用它。 (这会与 MVVM 架构风格相矛盾)
<?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>
最初我使用 <fx:define>
从 fxml 文件设置对 MenuText 的引用,但这不允许私有构造函数。应该没那么难,因为 MenuText 是静态的,但我无法对它的单例进行静态引用。
我试过了<Button text="${MenuText.getInstance().text}">
更新
如 回答中所述,我不应该使用单例模式。
基于 this 我添加了一个 ApplicationFactory:
//Creation of items with application lifetime
public class ApplicationFactory {
private Config config;
public void build() {
config = new Config();
}
public Config getConfig() {
return config;
}
}
这是正确的做法吗?我现在有一个 MenuFactory,它也是在 JavaFX start()
方法中创建的。它设置场景的父级。
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) {
//...
}
}
}
start() 方法如下所示:
@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();
}
这让事情变得更加复杂,我不确定这是否正确。此外,我仍然不知道如何在 fxml 文件中设置 MenuText。我试过了,但我认为这不是在 fxml 中设置命名空间的正确方法。
<fx:define>
<MenuText fx:id="menuText"/>
</fx:define>
我读过 these documentations 但不明白如何设置这个自定义命名空间。
请注意单例模式是 widely considered to be an anti-pattern。但是,如果你真的想这样做:
Initially I used <fx:define>
to setup a reference to the MenuText
from the fxml file
这是正确的做法。您可以将它与 fx:factory
结合使用,以获取对没有 (public) 默认构造函数的 class 实例的引用:
<fx:define>
<MenuText fx:factory="getInstance" fx:id="menuText" />
</fx:define>
然后
<Button text="${menuText.text}" />
另一个解决方案是“手动”将 MenuText
实例插入 FXML 命名空间:
MenuText menuText = ... ;
FXMLLoader loader = new FXMLLoader(getClass().getResource(...));
loader.getNamespace().put("menuText", menuText);
Parent root = loader.load();
然后
<Button text="${menuText.text}" />
无需额外的 FXML 代码即可使用。这种方法允许您避免使用单例模式(您基本上是将依赖项注入 FXML,因此您可以轻松地将它与 DI 框架结合起来)。
我有一个叫MenuText
的单身人士。它负责在菜单中显示正确的文本。它会动态更新。
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);
}
}
我有一个 fxml 文件,但 MenuText 无法引用它。 (这会与 MVVM 架构风格相矛盾)
<?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>
最初我使用 <fx:define>
从 fxml 文件设置对 MenuText 的引用,但这不允许私有构造函数。应该没那么难,因为 MenuText 是静态的,但我无法对它的单例进行静态引用。
我试过了<Button text="${MenuText.getInstance().text}">
更新
如
//Creation of items with application lifetime
public class ApplicationFactory {
private Config config;
public void build() {
config = new Config();
}
public Config getConfig() {
return config;
}
}
这是正确的做法吗?我现在有一个 MenuFactory,它也是在 JavaFX start()
方法中创建的。它设置场景的父级。
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) {
//...
}
}
}
start() 方法如下所示:
@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();
}
这让事情变得更加复杂,我不确定这是否正确。此外,我仍然不知道如何在 fxml 文件中设置 MenuText。我试过了,但我认为这不是在 fxml 中设置命名空间的正确方法。
<fx:define>
<MenuText fx:id="menuText"/>
</fx:define>
我读过 these documentations 但不明白如何设置这个自定义命名空间。
请注意单例模式是 widely considered to be an anti-pattern。但是,如果你真的想这样做:
Initially I used
<fx:define>
to setup a reference to theMenuText
from the fxml file
这是正确的做法。您可以将它与 fx:factory
结合使用,以获取对没有 (public) 默认构造函数的 class 实例的引用:
<fx:define>
<MenuText fx:factory="getInstance" fx:id="menuText" />
</fx:define>
然后
<Button text="${menuText.text}" />
另一个解决方案是“手动”将 MenuText
实例插入 FXML 命名空间:
MenuText menuText = ... ;
FXMLLoader loader = new FXMLLoader(getClass().getResource(...));
loader.getNamespace().put("menuText", menuText);
Parent root = loader.load();
然后
<Button text="${menuText.text}" />
无需额外的 FXML 代码即可使用。这种方法允许您避免使用单例模式(您基本上是将依赖项注入 FXML,因此您可以轻松地将它与 DI 框架结合起来)。