【问题标题】:Getters for FXML controllers in the controller class instead of lookup?控制器类中 FXML 控制器的获取器而不是查找?
【发布时间】:2016-09-05 12:25:24
【问题描述】:

我正在start() 中加载 fxmls,我想将它们放置在具有边框窗格的主窗口中。

        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(Main.class.getResource("Resources/Game.fxml"));
        Parent root = loader.load();
        GameController gameManagerController = loader.getController();

        loader = new FXMLLoader();
        loader.setLocation(getClass().getResource("Resources/TopMenuBar.fxml"));
        Parent topMenuBar = loader.load();
        TopMenuBarController topMenuBarController = loader.getController();

现在在root 中查找borderPane 返回null,即使我确实给了它一个id(不是fx:id)。所以我的问题是,在gameManagerController 中为borderPane 编写一个getter 并执行类似的操作是否可以接受?

gameManagerController.getBorderPane().setTop(topMenuBar);

【问题讨论】:

  • 从控制器中公开 UI 元素通常不是一个好主意(尽管它可能比使用非常脆弱的查找更好)。至少,在GameController 中定义一个方法来显示菜单,这样您就可以调用gameManagerController.showMenu(topMenuBar)(将方法命名为适当的名称)。您也可以考虑使用“视图模型”,但在这种情况下可能有点过头了。
  • showMenu() 会做什么?它会这样做吗:BorderPane.setTop(topMenuBar);? @James_D
  • 我会用控制器中更合适的字段名称替换BorderPane,但是是的,这就是想法。关键是,如果您决定更改布局的管理方式,所有更改都仅限于控制器和 FXML,您无需更改除此之外的代码。使用您建议的解决方案,如果您以后需要,维护或更改您的 UI 会变得更加困难。
  • 当然,我对所有内容都使用有意义的名称。我现在明白了,再次感谢。 @James_D

标签: java javafx java-8 javafx-8


【解决方案1】:

在控制器之外公开 UI 元素通常不是一个好主意。这样做会使以后更改 UI 的组织方式变得更加困难,因为您可能有控制器-FXML 对之外的代码,这些代码取决于特定的布局或存在的特定 UI 元素类。

一个简单但更健壮的解决方案,就是定义一个用于在GameController中显示菜单的方法:

public class GameController {

    @FXML
    private BorderPane rootPane ;

    // ...

    public void showMenu(Node menu) {
        rootPane.setTop(menu);
    }

    // ...
}

现在你可以做

gameManagerController.showMenu(topMenuBar);

一种更复杂的方法是使用“视图模型”来表示应用程序的视图状态,这对于这种情况可能有点过头但仍然值得一看。

public class ApplicationViewState {

    private final ObjectProperty<Node> menu = new SimpleObjectProperty<>();
    private final ObjectProperty<Node> content = new SimpleObjectProperty<>();

    public ObjectProperty<Node> menuProperty() {
        return menu ;
    }

    public final Node getMenu() {
        return menuProperty().get();
    }

    public final void setMenu(Node menu) {
        menuProperty().set(menu);
    }


    public ObjectProperty<Node> contentProperty() {
        return content ;
    }

    public final Node getContent() {
        return contentProperty().get();
    }

    public final void setContent(Node content) {
        contentProperty().set(content);
    }

    // ...
}

现在您可以在控制器之间共享视图模型的单个实例,控制器可以根据需要观察和更新状态:

public class GameController {

    @FXML
    private BorderPane rootPane ;

    private ApplicationViewState viewState ;

    public void setViewState(ApplicationViewState viewState) {
        this.viewState = viewState ;
        rootPane.topProperty().bind(viewState.menuProperty());
    }

    // ...
}

然后

FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("Resources/Game.fxml"));
Parent root = loader.load();
GameController gameManagerController = loader.getController();

loader = new FXMLLoader();
loader.setLocation(getClass().getResource("Resources/TopMenuBar.fxml"));
Parent topMenuBar = loader.load();
TopMenuBarController topMenuBarController = loader.getController();


ApplicationViewState viewState = new ApplicationViewState();
gameManagerController.setViewState(viewState);
viewState.setMenu(topMenuBar);

这种方法的优点是它为任何控制器提供了一种机制来更新应用程序其他部分的 UI,而无需“了解”其他控制器。这有助于解耦 UI 的各个部分。缺点是增加了应用程序的复杂性 - 可能很难调试,因为不太清楚 UI 中发生了哪些不同的事情。

【讨论】:

  • “ApplicationViewState”是门面模式吗?
  • 否;并不真地。它实际上只是 MVC 中的一个模型。
猜你喜欢
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 1970-01-01
  • 2013-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多