从控制器访问FXML中的重复控件,而无需为每个控件提供ID

时间:2017-12-30 19:32:32

标签: java javafx javafx-8 javafx-2

这就是我的 FXML 层次结构:

  • AnchorPane
    • 垂直框
      • HBox中
        • ComboBox:String:[Channel Name]
        • TextField:[开始前的延迟]
        • 按钮:[触发录制开始]
      • HBox中
        • ComboBox:String:[Channel Name]
        • TextField:[开始前的延迟]
        • 按钮:[触发录制开始]
      • HBox中
        • ComboBox:String:[Channel Name]
        • TextField:[开始前的延迟]
        • 按钮:[触发录制开始]

我想要一种方法来获取每个hbox -in my controller-内部的控件,而不必为其中的每个控件提供id。

  

我目前正在做的是使用每个元素的索引来获取a   坚持这样:

@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中有更多控件,或者如果我决定更改他们在层次结构中的位置,我使用的方法可能会乏味

有更好的方法吗?

1 个答案:

答案 0 :(得分:2)

使用HBoxComboBoxTextField创建一个单独的FXML文件(带有自己的控制器类),代表Button。然后使用<fx:include>将其包含在主FXML文件中。

如果需要,您可以使用"Nested Controllers"技术在“主”控制器中引用为所包含的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());
        // ...
    }
}

然后在你的“主”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> s:

<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文件和控制器类的创建角色(因此,您不必加载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>

您可以将fx:id添加到<ChannelControls>元素,并根据需要将它们直接注入主控制器。这种方法可以更轻松地公开ChannelControls类中的属性和方法,并在主控制器imho中访问它们。