我正在创建一个应用程序,该应用程序应指导用户进行操作,如以下屏幕截图所示:

java - 创建自定义控件以包含其他自定义控件-LMLPHP

  <Step fx:id="selectConnectionStep" source="SelectConnection" stepLabel="Step 1 : Select a Connection" />
  <Step fx:id="selectSObjectStep" source="SelectSObject" stepLabel="Step 2 : Select an SObject" />
  <Step fx:id="selectActionStep" stepLabel="Step 3 : Select an Action">
      <VBox GridPane.columnSpan="2" GridPane.rowIndex="1">
          <Action actionLabel="test" onAction="#test" />
          <GridPane.margin>
              <Insets bottom="15.0" left="15.0" right="10.0" top="10.0" />
          </GridPane.margin>
      </VBox>
  </Step>


在该应用程序中,我配置了更多场景以这种方式工作,因此我想以一种易于维护的方式进行设计。

我创建了名为“ Step”的自定义控件(基本上是GridPane的扩展)

每个“步骤”都有标题,loadingIcon和内部组件。

在前两个步骤中,我使用创建的'source'属性加载内部组件。我只给出组件的FXML文件的名称。在代码中,我只是加载FXML并将组件放在正确的位置。
但是,从第3步可以看出,我尝试将内部组件填充为Step子级。可以看出,这是可行的,但是我希望Step将其子代注入正确的位置,而不指定将其放置在网格内的位置的任何详细信息。



<Step fx:id="selectActionStep" stepLabel="Step 3 : Select an Action">
      <VBox>
          <Action actionLabel="test" onAction="#test" />
      </VBox>
</Step>


如果我不指定任何详细信息,则该组件将位于左上角的GridPane内部。

问题是自定义控件如何操作其FXML子元素?
我可以扩展GridPane吗?

谢谢 :)

最佳答案

最好的方法是在添加“标准”元素后,将ListChangeListener添加到子列表中,例如

private static final Insets MARGIN = new Insets(10, 10, 15, 15);

...

// add standard elements...

getChildren().addListener((ListChangeListener.Change<? extends Node> c) -> {
    while (c.next()) {
        if (c.wasAdded()) {
             for (Node n : c.getAddedSubList()) {
                 GridPane.setColumnSpan(2);
                 GridPane.setRowIndex(1);
                 GridPane.setMargin(MARGIN);
             }
        }
    }
});

07-28 13:08