【问题标题】:JavaFX 1 FXML File with Multiple Different Controllers?具有多个不同控制器的 JavaFX 1 FXML 文件?
【发布时间】:2013-04-12 06:46:01
【问题描述】:

在我的应用程序中有两个不同的阶段,它们是使用相同 FXML 文件的帮助屏幕。我不想创建 2 个 FXML 文件,而是只使用一个并拥有两个调用相同 fxml 的控制器。

唯一的问题是控制器是在 FXML 文件中分配的。那么,有没有办法用 Controller 类本身的代码更改分配的控制器?

我真的很想避免复制 FXML 文件只是为了更改每个中的控制器。提前致谢。


【问题讨论】:

    标签: controller javafx fxml


    【解决方案1】:

    您可以从FXML 文件中删除fx:controller="" 分配,并在加载期间通过FXMLLoader 分配控制器。

    FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Your.fxml"));
    fxmlLoader.setController(this);
    
    try
    {
        fxmlLoader.load();
    }
    catch (IOException exception)
    {
        throw new RuntimeException(exception);
    }
    

    查看Introduction to FXML section on custom components。

    【讨论】:

    • 使用这种方法会阻止您使用@FXML 标签。您必须手动设置它们,这在嵌套视图时可能很麻烦。
    【解决方案2】:

    在 FXML 文件中,<fx:include> 标记可以引用多个其他 fxml 文件。每个 fxml 文件都可以使用 fx:controller 属性定义自己的控制器。 在 fx:controller 标记内,给定类型而不是实例,因此在使用 FXML 加载器解析它时可能会使用自定义 Controllerfactory。

    setControllerFactor 函数需要一个 Callback 类型,这是一个只有一个可调用函数的接口:工厂使用的 call。该函数(由 FXMLLoader 类调用)期望获得一个 Class 对象作为输入参数,并根据类型提供一个实例化对象。以上Java8 lambdas可用于提供工厂:

    FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(fxmlFile));
    loader.setControllerFactory((Class<?> controllerType) ->{
        if(controllerType == Controller.class){
            return new Controller();
        }
    });
    Parent root = (Parent) fxmlLoader.load();
    

    上述代码中的参数controllerType是fxmlfx:controller属性提供的类型,由Java Class loader决定。当调用 Controllerfactory 时,还没有实例化任何内容,这就是为什么在 fxml 中甚至可以给出抽象类的原因。为了实现不同的行为,可以使用继承。

    一个例子是:

    class Controller{...}
    class FirstController extends Controller{...}
    class SecondController extends Controller{...}
    

    工厂可以这样使用:

    FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(fxmlFile));
    final int[] instantiatedClasses  = {0};
    loader.setControllerFactory((Class<?> controllerType) ->{
        if(controllerType == Controller.class){
            if(0 == instantiatedClasses[0]){
                ++instantiatedClasses[0];
                return new FirstController();
            } else return new SecondController();
        }
    });
    Parent root = (Parent) fxmlLoader.load();
    

    请注意,这种方式也可以向控制器提供不同的参数,因此继承可能是一种矫枉过正。 例如,primaryStage 可以提供给控制器,例如以消除对控制器中不同设置器的需要。

    class Controller{
        public Controller(int behaviorParam){...}
    }
    
    FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(fxmlFile));
    final int[] instantiatedClasses  = {-1}; /* so it would start at 0 */
    loader.setControllerFactory((Class<?> controllerType) ->{
        if(controllerType == Controller.class){
            ++instantiatedClasses[0]; 
            return new Controller(instantiatedClasses[0]);
        }
    });
    Parent root = (Parent) fxmlLoader.load();
    

    然而,这种方法的挑战在于,区分同一控制器类型的不同 fxml 实例仍然不是很简单。计算实例化类的数量是一种可行的方法,但与任何基于标识符的方法相比,它并不能提供太多控制。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-10-20
      • 2015-06-03
      • 2013-05-28
      • 2013-12-25
      • 2015-05-29
      • 2013-07-22
      相关资源
      最近更新 更多