在JavaFX-8中,可以将控制器动态添加到不是使用FXML / FXMLLoader创建的节点吗?(In JavaFX-8, can a controller be dynamically added to a node not created using FXML/FXMLLoader?)

这可能是newb-swimming-upstream的情况,但......

在FXML文件中,其中一个属性标识控制器。 我假设控制器和已识别的方法在加载期间绑定到节点(即,实例化控制器实例并且其方法绑定为FXML中标识的侦听器)。

有没有办法以编程方式将控制器实例与通过FXMLLoader创建程序性的JavaFX节点(例如,TableView)相关联?

This may be a case of newb-swimming-upstream, but...

In the FXML file, one of the attributes identifies the controller. I am assuming that the controller and identified methods are bound to the node during load (i.e., a controller instance is instantiated and its methods bound as listeners as identified in the FXML).

Is there a way to programmatically associate a controller instance with a JavaFX node (e.g., TableView) that was created procedurally vice via FXMLLoader?

最满意答案

如果我理解正确,你想用Java代码定义控制器实例,而不是让FXMLLoader实例化它。

您可以通过在调用load()之前调用setController上的FXMLLoader来完成此操作。 请注意,这意味着您必须创建FXMLLoader实例并调用实例 load()方法,并且不得调用静态 load(URL)方法:

MyController controller = new MyController(); FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml")); loader.setController(controller); Parent root = loader.load();

如果使用此技术,则不得在FXML文件的根元素中使用fx:controller属性。

请注意,这使您能够以任何方式实例化控制器 - 例如,如果您的控制器类具有接受参数的构造函数,则可以在此处使用它们(默认机制使用的控制器必须具有零参数构造函数)。

更高级但相关的信息:

相关技术,但也许对于更专业的用例,是在FXMLLoader上设置控制器工厂。 控制器工厂是将Class<?> (由fx:controller属性定义的那个)映射到对象(控制器)的函数。 这实际上为您提供了对控制器实例化方式的编程控制(通常使用反射)。 一个用途是如果你有一个模型类:

public class Model { /* ... */ }

和几个不同的控制器类,它们将模型引用作为构造函数参数。 通常,您希望使用相同的模型实例并将其传递给所有控制器。 因此,您可以按如下方式定义控制器工厂:

Model model = new Model(); Callback<Class<?>, Object> controllerFactory = type -> { try { // look for a constructor with a single parameter of type Model: for (Constructor<?> c : type.getConstructors) { if (c.getParameterCount() == 1 && c.getParameterTypes()[0] == Model.class) { return c.newInstance(model); } } // no constructor found, just invoke no-arg constructor as in default: return type.newInstance(); } catch (Exception e) { throw new RuntimeException(e); } };

然后,您可以在FXMLLoader上设置控制器工厂:

FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml")); loader.setControllerFactory(controllerFactory); Parent root = loader.load();

此技术的另一个用途是与依赖注入框架(如Spring和Guice)结合使用。 如果您配置DI框架为您实例化控制器,并可能将模型实例注入其中,您可以使用控制器工厂允许FXMLLoader从框架中检索控制器实例。 例如春天:

ApplicationContext context = ... ; FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml")); loader.setControllerFactory(context::getBean); Parent root = loader.load();

或与Guice:

Injector injector = Guice.createInjector(...); FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml")); loader.setControllerFactory(injector::getInstance); Parent root = loader.load();

If I understand correctly, you want to define the controller instance in Java code, instead of letting the FXMLLoader instantiate it.

You can do this by calling setController on the FXMLLoader before you call load(). Note that this means you must create an FXMLLoader instance and invoke the instance load() method, and must not call the static load(URL) method:

MyController controller = new MyController(); FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml")); loader.setController(controller); Parent root = loader.load();

If you use this technique, you must not use the fx:controller attribute in the root element of the FXML file.

Note that this gives you the ability to instantiate the controller any way you like - e.g. if you have controller classes which have constructors that take parameters you can use them here (controllers used by the default mechanism must have a zero-arg constructor).

More advanced but related information:

A related technique, but perhaps for more specialized use cases, is to set a controller factory on the FXMLLoader. The controller factory is a function that maps a Class<?> (the one defined by the fx:controller attribute) to an object (the controller). This essentially gives you programmatic control over the way the controllers are instantiated (typically using reflection). One use of this is if you have a model class:

public class Model { /* ... */ }

and several different controller classes that take a model reference as a constructor parameter. Typically you would want to use the same model instance and pass it to all controllers. So you could define a controller factory as follows:

Model model = new Model(); Callback<Class<?>, Object> controllerFactory = type -> { try { // look for a constructor with a single parameter of type Model: for (Constructor<?> c : type.getConstructors) { if (c.getParameterCount() == 1 && c.getParameterTypes()[0] == Model.class) { return c.newInstance(model); } } // no constructor found, just invoke no-arg constructor as in default: return type.newInstance(); } catch (Exception e) { throw new RuntimeException(e); } };

Then you can set the controller factory on your FXMLLoader(s):

FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml")); loader.setControllerFactory(controllerFactory); Parent root = loader.load();

Another use of this technique is in conjunction with dependency-injection frameworks such as Spring and Guice. If you configure a DI framework to instantiate controllers for you, and perhaps inject model instances into them, you can use the controller factory to allow the FXMLLoader to retrieve the controller instances from the framework. E.g. with Spring:

ApplicationContext context = ... ; FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml")); loader.setControllerFactory(context::getBean); Parent root = loader.load();

or with Guice:

Injector injector = Guice.createInjector(...); FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml")); loader.setControllerFactory(injector::getInstance); Parent root = loader.load();在JavaFX-8中,可以将控制器动态添加到不是使用FXML / FXMLLoader创建的节点吗?(In JavaFX-8, can a controller be dynamically added to a node not created using FXML/FXMLLoader?)

这可能是newb-swimming-upstream的情况,但......

在FXML文件中,其中一个属性标识控制器。 我假设控制器和已识别的方法在加载期间绑定到节点(即,实例化控制器实例并且其方法绑定为FXML中标识的侦听器)。

有没有办法以编程方式将控制器实例与通过FXMLLoader创建程序性的JavaFX节点(例如,TableView)相关联?

This may be a case of newb-swimming-upstream, but...

In the FXML file, one of the attributes identifies the controller. I am assuming that the controller and identified methods are bound to the node during load (i.e., a controller instance is instantiated and its methods bound as listeners as identified in the FXML).

Is there a way to programmatically associate a controller instance with a JavaFX node (e.g., TableView) that was created procedurally vice via FXMLLoader?

最满意答案

如果我理解正确,你想用Java代码定义控制器实例,而不是让FXMLLoader实例化它。

您可以通过在调用load()之前调用setController上的FXMLLoader来完成此操作。 请注意,这意味着您必须创建FXMLLoader实例并调用实例 load()方法,并且不得调用静态 load(URL)方法:

MyController controller = new MyController(); FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml")); loader.setController(controller); Parent root = loader.load();

如果使用此技术,则不得在FXML文件的根元素中使用fx:controller属性。

请注意,这使您能够以任何方式实例化控制器 - 例如,如果您的控制器类具有接受参数的构造函数,则可以在此处使用它们(默认机制使用的控制器必须具有零参数构造函数)。

更高级但相关的信息:

相关技术,但也许对于更专业的用例,是在FXMLLoader上设置控制器工厂。 控制器工厂是将Class<?> (由fx:controller属性定义的那个)映射到对象(控制器)的函数。 这实际上为您提供了对控制器实例化方式的编程控制(通常使用反射)。 一个用途是如果你有一个模型类:

public class Model { /* ... */ }

和几个不同的控制器类,它们将模型引用作为构造函数参数。 通常,您希望使用相同的模型实例并将其传递给所有控制器。 因此,您可以按如下方式定义控制器工厂:

Model model = new Model(); Callback<Class<?>, Object> controllerFactory = type -> { try { // look for a constructor with a single parameter of type Model: for (Constructor<?> c : type.getConstructors) { if (c.getParameterCount() == 1 && c.getParameterTypes()[0] == Model.class) { return c.newInstance(model); } } // no constructor found, just invoke no-arg constructor as in default: return type.newInstance(); } catch (Exception e) { throw new RuntimeException(e); } };

然后,您可以在FXMLLoader上设置控制器工厂:

FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml")); loader.setControllerFactory(controllerFactory); Parent root = loader.load();

此技术的另一个用途是与依赖注入框架(如Spring和Guice)结合使用。 如果您配置DI框架为您实例化控制器,并可能将模型实例注入其中,您可以使用控制器工厂允许FXMLLoader从框架中检索控制器实例。 例如春天:

ApplicationContext context = ... ; FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml")); loader.setControllerFactory(context::getBean); Parent root = loader.load();

或与Guice:

Injector injector = Guice.createInjector(...); FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml")); loader.setControllerFactory(injector::getInstance); Parent root = loader.load();

If I understand correctly, you want to define the controller instance in Java code, instead of letting the FXMLLoader instantiate it.

You can do this by calling setController on the FXMLLoader before you call load(). Note that this means you must create an FXMLLoader instance and invoke the instance load() method, and must not call the static load(URL) method:

MyController controller = new MyController(); FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml")); loader.setController(controller); Parent root = loader.load();

If you use this technique, you must not use the fx:controller attribute in the root element of the FXML file.

Note that this gives you the ability to instantiate the controller any way you like - e.g. if you have controller classes which have constructors that take parameters you can use them here (controllers used by the default mechanism must have a zero-arg constructor).

More advanced but related information:

A related technique, but perhaps for more specialized use cases, is to set a controller factory on the FXMLLoader. The controller factory is a function that maps a Class<?> (the one defined by the fx:controller attribute) to an object (the controller). This essentially gives you programmatic control over the way the controllers are instantiated (typically using reflection). One use of this is if you have a model class:

public class Model { /* ... */ }

and several different controller classes that take a model reference as a constructor parameter. Typically you would want to use the same model instance and pass it to all controllers. So you could define a controller factory as follows:

Model model = new Model(); Callback<Class<?>, Object> controllerFactory = type -> { try { // look for a constructor with a single parameter of type Model: for (Constructor<?> c : type.getConstructors) { if (c.getParameterCount() == 1 && c.getParameterTypes()[0] == Model.class) { return c.newInstance(model); } } // no constructor found, just invoke no-arg constructor as in default: return type.newInstance(); } catch (Exception e) { throw new RuntimeException(e); } };

Then you can set the controller factory on your FXMLLoader(s):

FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml")); loader.setControllerFactory(controllerFactory); Parent root = loader.load();

Another use of this technique is in conjunction with dependency-injection frameworks such as Spring and Guice. If you configure a DI framework to instantiate controllers for you, and perhaps inject model instances into them, you can use the controller factory to allow the FXMLLoader to retrieve the controller instances from the framework. E.g. with Spring:

ApplicationContext context = ... ; FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml")); loader.setControllerFactory(context::getBean); Parent root = loader.load();

or with Guice:

Injector injector = Guice.createInjector(...); FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml")); loader.setControllerFactory(injector::getInstance); Parent root = loader.load();