我使用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();
答案 0 :(得分:0)
可以在 FXML 加载器中使用自定义 Controllerfactory。
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
是fxml fx: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 实例仍然不是很简单。计算实例化类的数量是一种有效的方法,但与任何基于标识符的方法相比,它并没有提供太多控制。