JavaFX:检索节点 [英] JavaFX: Retrieve a Node
问题描述
我有两个 FXML
文件,每个文件代表一个视图,我们称之为 View1
和 View2
,我已将每一个放在一个单独的标签
中,( Tab1
和 Tab2
)在 TabPane
中。
现在在 Controller1
of View1
我有一个事件会切换 selectedItem
我的 TabPane
从 Tab1
到 Tab2
。我的问题是如何从 Controller1
<访问我的
TabPane
一般而言。我们如何在 Javafx
中检索某个节点
。 编辑
View1
< VBox fx:控制器= controllers.Controller1 >
< Button onAction =#openView2/>
< / VBox>
Controller1
public class Controller1 {
public void openView2(){
//我该怎么办
}
}
MainView
< TabPane fx:id = tabPanefx:controller =controllers.MainController/>
MainController
public class MainController实现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自动生成的catch块
e.printStackTrace();
}
}
protected Tab createView1Tab()抛出IOException {
Tab tab = new Tab();
tab.setContent(FXMLLoader.load(getClass()。getResource(/ views / View1.fxml)));
返回标签;
}
protected Tab createView2Tab()抛出IOException {
Tab tab = new Tab();
tab.setContent(FXMLLoader.load(getClass()。getResource(/ views / View2.fxml)));
返回标签;
}
}
你应创建一个视图模型,它封装视图的当前状态,并与每个控制器共享。然后从主控制器中观察并做出相应的响应。
例如:
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);
}
}
现在你可以做(注意我也删除了多余的)这里重复的代码):
公共类MainController实现Initializable {
@FXML
public TabPane tabPane;
private final ApplicationState appState = new ApplicationState();
私人最终地图< String,Tab> views = new HashMap<>();
@Override
public void initialize(URL arg0,ResourceBundle arg1){
try {
tabPane.getTabs()。add(createViewTab(View1,new控制器1(APPSTATE)));
tabPane.getTabs()。add(createViewTab(View2,new Controller2(appState)));
} catch(IOException e){
// TODO自动生成的catch块
e.printStackTrace();
}
appState.currentViewNameProperty()。addListener((obs,oldView,newView) - >
tabPane.getSelectionModel()。select(views.get(newView)));
appState.setCurrentViewName(View1);
}
protected Tab createViewTab(String viewName,Object controller)抛出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);
返回标签;
}
}
现在你的控制器只需要这样做:
public class Controller1 {
private final ApplicationState appState;
public Controller1(ApplicationState appState){
this.appState = appState;
}
public void openView2(){
appState.setCurrentViewName(View2);
}
}
请注意,由于控制器没有 - arg构造函数,我在代码中用 loader.setController(...)
设置它们。这意味着您必须从view1和view2的fxml文件中删除 fx:controller
属性,例如你的View1.fxml变为:
< VBox xmlns =...> <! - no fx:controller here - >
< Button onAction =#openView2/>
< / VBox>
这种设计的优势在于,当你的老板在8个月内走进办公室并说客户不喜欢选项卡窗格,将其替换为一次只显示一个屏幕的内容,因为所有内容都正确分离,所以很容易进行更改。 (您只需要更改主视图及其控制器,其他任何视图或控制器都不会发生任何变化。)如果将选项卡窗格暴露给所有其他控制器,则必须找到您访问过的所有位置它可以做出类似的改变。
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;
}
}
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.
这篇关于JavaFX:检索节点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!