JavaFX: Retrieve a Node

自作多情 提交于 2019-12-25 07:58:38

问题


I have two FXML documents each one represents a view, let's call them View1 and View2, and I have placed each one in a separate Tab, (Tab1 and Tab2) inside a TabPane.

Now in the Controller1 of View1 I have an event that will switch the selectedItem of my TabPane from Tab1 to Tab2. My question is how can I access my TabPane from Controller1

In general. How do we retrieve a certain Node in Javafx.

Edit View1

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

Controller1

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

MainView

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

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

回答1:


You should create a "view model" which encapsulates the current state of the view, and share it with each of the controllers. Then observe it from your main controller and respond accordingly.

For example:

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

Now you can do (note I also removed your redundant repetitive code here):

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


}

Now your controllers just have to do:

public class Controller1{

    private final ApplicationState appState ;

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

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

Note that since the controllers don't have no-arg constructors, I am setting them in code with loader.setController(...). This means you have to remove the fx:controller attribute from the fxml files for view1 and view2, e.g. your View1.fxml becomes:

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

The advantage of this design is that when your boss walks into your office in 8 months and says "The customer doesn't like the tab pane, replace it with something that only shows one screen at a time", it's very easy to make changes like that as everything is properly decoupled. (You would only have to change the main view and its controller, none of the other views or controllers would change at all.) If you exposed the tab pane to all the other controllers, you would have to find all the places you had accessed it to make changes like that.



来源:https://stackoverflow.com/questions/41285805/javafx-retrieve-a-node

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!