JavaFX:检索节点

JavaFX: Retrieve a Node

我有两个 FXML 个文档,每个文档代表一个视图,我们称它们为 View1View2,我将每个文档放在一个单独的 Tab 中, (Tab1Tab2) 在 TabPane.

现在在 View1Controller1 中,我有一个事件可以将我的 TabPaneselectedItemTab1 切换到 Tab2.我的问题是如何从 Controller1

访问我的 TabPane

一般。我们如何检索 Javafx.

中的某个 Node

编辑 视图 1

<VBox fx:controller="controllers.Controller1">
    <Button onAction="#openView2"/>
</VBox>

控制器 1

public class Controller1{
    public void openView2(){
        //What should I do here
    }
}

主视图

<TabPane fx:id="tabPane" fx:controller="controllers.MainController"/>

主控制器

public class MainController implements Initializable {

@FXML
public TabPane tabPane;

@Override
public void initialize(URL arg0, ResourceBundle arg1) {
    try {
        tabPane.getTabs().add(createView1Tab());
        tabPane.getTabs().add(createView2Tab());
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

protected Tab createView1Tab() throws IOException {
    Tab tab = new Tab();
    tab.setContent(FXMLLoader.load(getClass().getResource("/views/View1.fxml")));
    return tab;
}

protected Tab createView2Tab() throws IOException {
    Tab tab = new Tab();
    tab.setContent(FXMLLoader.load(getClass().getResource("/views/View2.fxml")));
    return tab;
}
}

您应该创建一个 "view model" 来封装视图的当前状态,并与每个控制器共享。然后从你的主控制器观察它并做出相应的反应。

例如:

public class ApplicationState {

    private final StringProperty currentViewName = new SimpleStringProperty();

    public StringProperty currentViewNameProperty() {
        return currentViewName ;
    }

    public final String getCurrentViewName() {
        return currentViewNameProperty().get();
    }

    public final void setCurrentViewName(String viewName) {
        currentViewNameProperty().set(viewName);
    }
}

现在你可以做(​​注意我在这里也删除了你多余的重复代码):

public class MainController implements Initializable {

    @FXML
    public TabPane tabPane;

    private final ApplicationState appState = new ApplicationState();
    private final Map<String, Tab> views = new HashMap<>();

    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {
        try {
            tabPane.getTabs().add(createViewTab("View1", new Controller1(appState)));
            tabPane.getTabs().add(createViewTab("View2", new Controller2(appState)));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        appState.currentViewNameProperty().addListener((obs, oldView, newView) ->
            tabPane.getSelectionModel().select(views.get(newView)));
        appState.setCurrentViewName("View1");
    }

    protected Tab createViewTab(String viewName, Object controller) throws IOException {
        Tab tab = new Tab();
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/views/"+viewName+".fxml"));
        loader.setController(controller);
        tab.setContent(loader.load());
        views.put(viewName, tab);
        return tab;
    }


}

现在您的控制器只需要做:

public class Controller1{

    private final ApplicationState appState ;

    public Controller1(ApplicationState appState) {
        this.appState = appState ;
    }

    public void openView2(){
        appState.setCurrentViewName("View2");
    }
}

请注意,由于控制器没有无参数构造函数,我在代码中使用 loader.setController(...) 设置它们。这意味着您必须从 view1 和 view2 的 fxml 文件中删除 fx:controller 属性,例如你的 View1.fxml 变成:

<VBox xmlns="..."> <!-- no fx:controller here -->
     <Button onAction="#openView2"/>
</VBox>

这种设计的优势在于,当您的老板在 8 个月后走进您的办公室并说 "The customer doesn't like the tab pane, replace it with something that only shows one screen at a time" 时,由于一切都已适当解耦,因此很容易做出这样的更改。 (您只需要更改主视图及其控制器,其他视图或控制器的 none 将完全改变。)如果您将选项卡窗格公开给所有其他控制器,则必须找到所有您访问过的地方进行了类似的更改。