【问题标题】:JavaFx Generate multiple scene from single FXMLJavaFx 从单个 FXML 生成多个场景
【发布时间】:2026-01-11 12:50:02
【问题描述】:

我启动了一个与数据库交互的 javafx 项目(添加新、编辑、删除、查找...)。
我的数据库包含很多表,每个表都需要自己的场景(几乎一样!!!)。
所以我的问题是:不是实现 ~20 fxml 文件,是否可以制作一个单独的 fxml 文件,该文件将根据传递给其控制器的类名(例如)更改其内容?
如果是,有任何提示来实现它吗?
这是我尝试过的:
为每个 TableClass 添加了一个 HashMap,其中包含场景中需要的所有属性,并在 ControllerClass 中对其进行迭代以将控件添加到场景中!!!但是失败了!
TableSampleClass:

public class TableSampleClass{
public static final HashMap<String, String> attr;
static {
    attr = new HashMap<String, String>();
    attr.put("ref", "text");
    attr.put("name", "text");
    attr.put("adress", "text");
    attr.put("Mobile", "tel");
    attr.put("mail", "mail");
    attr.put("isalive", "checkbox");//just to illustrate what i want !
    }
     ........
   }

控制器类:

@FXML
private AnchorPane pane;
@Override
public void initialize(URL location, ResourceBundle resources)
{     Iterator it = TableSampleClass.attr.entrySet().iterator();
    while(it.hasNext())
    {
        Map.Entry pair = (Map.Entry)it.next();
        switch (pair.getValue().toString()){
        case "text":
            TextField txt =new TextField(pair.getValue().toString());
            txt.setPromptText(pair.getKey().toString());
            Label lbl =new Label(pair.getKey().toString());
            pane.getChildren().add(txt);
            break;
        }
    }
}

我希望我确实解释清楚了!!

【问题讨论】:

    标签: java javafx


    【解决方案1】:

    可能的方法

    有一些第三方库可以为 JavaFX 动态生成表单和界面 UI,例如 FXForm2ControlsFX PropertySheets

    也可以生成一个table from data dynamically,这看起来可能是你真正想要做的。

    关于 FXML

    对于大多数这些系统,您无需使用 FXML 定义小部件的内部 GUI。相反,低级字段是通过对数据或 Java 类的自省来动态生成的。您要做的就是将适当的库添加到 SceneBuilder 并导入高级控件(例如 ControlsFX PropertySheet),只需在 FXML 中声明它,而不是在 FXML 中定义控件的详细字段。

    如果需要,并且链接库中的系统不是您所需要的,您还可以创建自定义控件和 import those for use in SceneBuilder,尽管它们仍可能以类似的方式从数据或动态生成详细信息字段通过reflection 进行Java 类自省。

    关于通过自省生成动态字段

    基于反射创建自定义控件是一个高级主题,仅推荐给有经验的 Java 和 JavaFX 开发人员。您可以查看 PropertySheet implementation 例如这种方法的代码,该方法通过 BeanPropertyUtils 实用程序类使用 java beansIntrospector。 Java bean 是一个相当大的话题,我在这里无法详细介绍。只需要 Java bean 规范的一些特性来实现您所需要的,因为规范的其他部分已被更新的 JavaFX 特性(如属性和绑定)淘汰。

    【讨论】:

      【解决方案2】:

      使用Custom component FXML pattern。其工作方式是编写一个控制器类,在构造函数中加载相应的 FXML 文件。由于通过调用构造函数来使用 this,因此可以允许构造函数接受参数,并在加载 FXML 后使用这些参数来配置组件。

      你展示的代码对我来说不是很清楚,但这个结构看起来像:

      public class MyCustomTableDisplay extends AnchorPane {
      
          @FXML
          private AnchorPane pane ;
      
          public MyCustomTableDisplay(Map<String, String> config) {
              try {
                  FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml"));
                  loader.setRoot(this); 
                  loader.setController(this);
                  loader.load();
              } catch (IOException exc) {
                  throw new RuntimeException(exc);
              }
      
              // now configure pane based on config passed in...
          }
      }
      

      FXML 文件必须使用“动态根”,这意味着根元素是通过在FXMLLoader 上调用setRoot(...) 来确定的(如上面的代码所示)。所以看起来像

      <!-- imports omitted -->
      <fx:root type="AnchorPane" xmlns:fx="..." ... >
          <!-- usual fxml stuff -->
          <AnchorPane fx:id="pane" ...>
      
          <AnchorPane>
      </fx:root>
      

      现在你就去做

      Map<String, String> config = new HashMap<>();
      // populate config...
      MyCustomTableDisplay display = new MyCustomTableDisplay(config);
      Scene scene = new Scene(display);
      // etc...
      

      fx:roottype 基本上可以是任何节点类型,但必须与类的类型相匹配。然后你可以包含任何你想要的 fxml 内容。该类充当控制器,因此您可以以通常的方式从 FXML 中注入值:当在加载器上调用 load() 方法时,它们将被初始化(像往常一样)。

      【讨论】: