3

我使用 JavaFX Scene Builder/FXML 设计了一个场景,我想创建该场景的许多实例,但每个场景都有不同的行为。有没有办法动态更改场景/FXML 的控制器?

我想要的是设计一个场景并重用它,但每个实例都有不同的行为。


目前我正在加载 FXML 及其控制器,如下所示:

FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(fxmlFile));
Parent root = (Parent) fxmlLoader.load();
Scene scene = new Scene(root);
Controller controller = fxmlLoader.getController();
4

1 回答 1

0

可以将自定义Controllerfactory与 FXML 加载器一起使用。该setControllerFactory方法需要一个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 类加载器决定。当调用 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 实例仍然不是很简单。计算实例化类的数量是一种可行的方法,但与任何基于标识符的方法相比,它并不能提供太多控制。

于 2021-03-17T07:15:34.780 回答