JavaFX:检索节点
JavaFX: Retrieve a Node
我有两个 FXML
个文档,每个文档代表一个视图,我们称它们为 View1
和 View2
,我将每个文档放在一个单独的 Tab
中, (Tab1
和 Tab2
) 在 TabPane
.
内
现在在 View1
的 Controller1
中,我有一个事件可以将我的 TabPane
的 selectedItem
从 Tab1
切换到 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 将完全改变。)如果您将选项卡窗格公开给所有其他控制器,则必须找到所有您访问过的地方进行了类似的更改。
我有两个 FXML
个文档,每个文档代表一个视图,我们称它们为 View1
和 View2
,我将每个文档放在一个单独的 Tab
中, (Tab1
和 Tab2
) 在 TabPane
.
现在在 View1
的 Controller1
中,我有一个事件可以将我的 TabPane
的 selectedItem
从 Tab1
切换到 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 将完全改变。)如果您将选项卡窗格公开给所有其他控制器,则必须找到所有您访问过的地方进行了类似的更改。