在控制器类中使用FXML控制器而不是查找?

时间:2016-09-05 12:25:24

标签: java javafx java-8 javafx-8

我正在start()中加载fxmls,我想将它们放在主窗口中,它说的是borderPane。

        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);

1 个答案:

答案 0 :(得分:2)

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

一个简单但更强大的解决方案,就是定义一个方法,以便在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中发生的不同事情。