从控制器访问 FXML 中的重复控件,而无需为每个控件提供 ID
Access repetitive controls in FXML from the controller without having to give an ID to each control
这就是我的 FXML 层次结构:
- AnchorPane
- VBox
- HBox
- 组合框:字符串:[频道名称]
- TextField:[开始前延迟]
- 按钮:[触发录制开始]
- HBox
- 组合框:字符串:[频道名称]
- TextField:[开始前延迟]
- 按钮:[触发录制开始]
- HBox
- 组合框:字符串:[频道名称]
- TextField:[开始前延迟]
- 按钮:[触发录制开始]
我想要一种方法来访问每个 hbox -in my controller-
内部的控件,而不必为其中的每个控件提供 id。
What i'm currenty doing is using the index of each element to get a
hold of it like this:
@FXML
public void startRecording(MouseEvent event) {
ObservableList<Node> curChildNodes = ((Node) event.getTarget()).getParent().getChildrenUnmodifiable();
String channelName = ((ComboBox<String>) (curChildNodes.get(0))).getSelectionModel().getSelectedItem();
long delay = Long.parseLong(((TextField) curChildNodes.get(1)).getText());
Stream stream = new Stream(channelName, delay);
Recorder recorder = new Recorder(stream);
recorder.startAfterDelay();
}
如果我在该 HBox 中有更多控件,或者如果我决定更改它们在层次结构中的位置,我使用的方法可能会变得 乏味 。
有更好的方法吗?
创建一个单独的 FXML 文件(带有自己的控制器 class),用 ComboBox
、TextField
和 Button
表示 HBox
。然后使用 <fx:include>
将其包含在您的主 FXML 文件中。
如果需要,您可以使用 "Nested Controllers" 技术引用为 "main" 控制器中包含的 FXML 文件的每个实例创建的控制器实例。
所以你可以创建这样的东西(我称之为 ChannelControls.fxml
):
<HBox xmlns:fx="..." fx:controller="myapp.ChannelController">
<ComboBox fx:id="channel"/>
<TextField fx:id="delay"/>
<Button text="Start" fx:id="start" onAction="startRecording"/>
</HBox>
带控制器
public class ChannelController {
@FXML
private ComboBox<String> channel ;
@FXML
private TextField delay ;
@FXML
private Button start ;
@FXML
private void startRecording(ActionEvent event) {
String channelName = channel.getValue();
long delayTime = Long.parseLong(delay.getText());
// ...
}
}
然后在你的 "main" fxml 中,你可以做
<AnchorPane xmlns:fx="..." fx:controller="myapp.MainController" >
<VBox>
<fx:include src="ChannelControls.fxml"/>
<fx:include src="ChannelControls.fxml"/>
<fx:include src="ChannelControls.fxml"/>
</VBox>
</AnchorPane>
如果需要访问主控制器中ChannelController
的实例,将fx:id
添加到<fx:include>
中:
<fx:include src="ChannelControls.fxml" fx:id="channel1" />
<fx:include src="ChannelControls.fxml" fx:id="channel2" />
<!-- etc -->
然后您可以通过将 "Controller"
附加到 fx:id
值来访问控制器:
public class MainController {
@FXML
private ChannelController channel1Controller ;
@FXML
private ChannelController channel2Controller ;
public void initialize() {
// do anything you need with channel1Controller, etc.
}
}
一个细微的变体是将 HBox
的片段实现为 "Custom Component"。这实际上只是颠倒了 FXML 文件和控制器 class 的创建角色(因此,不是加载自动创建控制器的 FXML 文件,而是创建自动加载 FXML 的控制器)。所以你可以创造
public class ChannelControls extends HBox {
@FXML
private ComboBox<String> channel ;
@FXML
private TextField delay ;
@FXML
private Button start ;
public ChannelControls() {
try {
FXMLLoader loader = new FXMLLoader("ChannelControls.fxml");
loader.setRoot(this);
loader.setController(this);
loader.load();
} catch (IOException exc) {
// this is pretty much fatal:
throw new UncheckedIOException(exc);
}
}
@FXML
private void startRecording(ActionEvent event) {
String channelName = channel.getValue();
long delayTime = Long.parseLong(delay.getText());
// ...
}
// other methods as needed
}
对 ChannelControls.fxml
的唯一更改是根元素:请注意,您必须删除 fx:controller
属性:
<fx:root type="HBox" xmlns:fx="...">
<ComboBox fx:id="channel"/>
<TextField fx:id="delay"/>
<Button text="Start" fx:id="start" onAction="startRecording"/>
</fx:root>
现在您的主要 fxml 文件只需要
<AnchorPane xmlns:fx="..." fx:controller="myapp.MainController" >
<VBox>
<ChannelControls/>
<ChannelControls/>
<ChannelControls/>
</VBox>
</AnchorPane>
可以在<ChannelControls>
元素中添加fx:id
s,需要的话直接注入到主控制器中。这种方法使得在 ChannelControls
class 中公开属性和方法并在主控制器中访问它们变得稍微容易一些,恕我直言。
这就是我的 FXML 层次结构:
- AnchorPane
- VBox
- HBox
- 组合框:字符串:[频道名称]
- TextField:[开始前延迟]
- 按钮:[触发录制开始]
- HBox
- 组合框:字符串:[频道名称]
- TextField:[开始前延迟]
- 按钮:[触发录制开始]
- HBox
- 组合框:字符串:[频道名称]
- TextField:[开始前延迟]
- 按钮:[触发录制开始]
- HBox
- VBox
我想要一种方法来访问每个 hbox -in my controller-
内部的控件,而不必为其中的每个控件提供 id。
What i'm currenty doing is using the index of each element to get a hold of it like this:
@FXML
public void startRecording(MouseEvent event) {
ObservableList<Node> curChildNodes = ((Node) event.getTarget()).getParent().getChildrenUnmodifiable();
String channelName = ((ComboBox<String>) (curChildNodes.get(0))).getSelectionModel().getSelectedItem();
long delay = Long.parseLong(((TextField) curChildNodes.get(1)).getText());
Stream stream = new Stream(channelName, delay);
Recorder recorder = new Recorder(stream);
recorder.startAfterDelay();
}
如果我在该 HBox 中有更多控件,或者如果我决定更改它们在层次结构中的位置,我使用的方法可能会变得 乏味 。
有更好的方法吗?
创建一个单独的 FXML 文件(带有自己的控制器 class),用 ComboBox
、TextField
和 Button
表示 HBox
。然后使用 <fx:include>
将其包含在您的主 FXML 文件中。
如果需要,您可以使用 "Nested Controllers" 技术引用为 "main" 控制器中包含的 FXML 文件的每个实例创建的控制器实例。
所以你可以创建这样的东西(我称之为 ChannelControls.fxml
):
<HBox xmlns:fx="..." fx:controller="myapp.ChannelController">
<ComboBox fx:id="channel"/>
<TextField fx:id="delay"/>
<Button text="Start" fx:id="start" onAction="startRecording"/>
</HBox>
带控制器
public class ChannelController {
@FXML
private ComboBox<String> channel ;
@FXML
private TextField delay ;
@FXML
private Button start ;
@FXML
private void startRecording(ActionEvent event) {
String channelName = channel.getValue();
long delayTime = Long.parseLong(delay.getText());
// ...
}
}
然后在你的 "main" fxml 中,你可以做
<AnchorPane xmlns:fx="..." fx:controller="myapp.MainController" >
<VBox>
<fx:include src="ChannelControls.fxml"/>
<fx:include src="ChannelControls.fxml"/>
<fx:include src="ChannelControls.fxml"/>
</VBox>
</AnchorPane>
如果需要访问主控制器中ChannelController
的实例,将fx:id
添加到<fx:include>
中:
<fx:include src="ChannelControls.fxml" fx:id="channel1" />
<fx:include src="ChannelControls.fxml" fx:id="channel2" />
<!-- etc -->
然后您可以通过将 "Controller"
附加到 fx:id
值来访问控制器:
public class MainController {
@FXML
private ChannelController channel1Controller ;
@FXML
private ChannelController channel2Controller ;
public void initialize() {
// do anything you need with channel1Controller, etc.
}
}
一个细微的变体是将 HBox
的片段实现为 "Custom Component"。这实际上只是颠倒了 FXML 文件和控制器 class 的创建角色(因此,不是加载自动创建控制器的 FXML 文件,而是创建自动加载 FXML 的控制器)。所以你可以创造
public class ChannelControls extends HBox {
@FXML
private ComboBox<String> channel ;
@FXML
private TextField delay ;
@FXML
private Button start ;
public ChannelControls() {
try {
FXMLLoader loader = new FXMLLoader("ChannelControls.fxml");
loader.setRoot(this);
loader.setController(this);
loader.load();
} catch (IOException exc) {
// this is pretty much fatal:
throw new UncheckedIOException(exc);
}
}
@FXML
private void startRecording(ActionEvent event) {
String channelName = channel.getValue();
long delayTime = Long.parseLong(delay.getText());
// ...
}
// other methods as needed
}
对 ChannelControls.fxml
的唯一更改是根元素:请注意,您必须删除 fx:controller
属性:
<fx:root type="HBox" xmlns:fx="...">
<ComboBox fx:id="channel"/>
<TextField fx:id="delay"/>
<Button text="Start" fx:id="start" onAction="startRecording"/>
</fx:root>
现在您的主要 fxml 文件只需要
<AnchorPane xmlns:fx="..." fx:controller="myapp.MainController" >
<VBox>
<ChannelControls/>
<ChannelControls/>
<ChannelControls/>
</VBox>
</AnchorPane>
可以在<ChannelControls>
元素中添加fx:id
s,需要的话直接注入到主控制器中。这种方法使得在 ChannelControls
class 中公开属性和方法并在主控制器中访问它们变得稍微容易一些,恕我直言。