解决方案1:
HostServices通过您的应用程序向下传递引用。
这可能类似于您预期的“非常痛苦”的方法。但基本上,您会执行以下操作:
public void start(Stage primaryStage) throws Exception { FXMLLoader loader = new FXMLLoader(getClass().getResource("main.fxml")); Parent root = loader.load(); MainController controller = loader.getController(); controller.setHostServices(getHostServices()); primaryStage.setScene(new Scene(root)); primaryStage.show();}然后在
MainController:
public class MainController { private HostServices hostServices ; public HostServices getHostServices() { return hostServices ; } public void setHostServices(HostServices hostServices) { this.hostServices = hostServices ; } @FXML private void showDialog() { FXMLLoader loader = new FXMLLoader(getClass().getResource("dialog.fxml")); Parent dialogRoot = loader.load(); DialogController dialogController = loader.getController(); dialogController.setHostServices(hostServices); Stage dialog = new Stage(); dialog.setScene(new Scene(dialogRoot)); dialog.show(); }}当然
DialogController看起来像:
public class DialogController { @FXML private Hyperlink hyperlink ; private HostServices hostServices ; public HostServices getHostServices() { return hostServices ; } public void setHostServices(HostServices hostServices) { this.hostServices = hostServices ; } @FXML private void openURL() { hostServices.opendocument(hyperlink.getText()); }}解决方案2: 使用控制器工厂将主机服务推送到控制器。
这是上面的更干净的版本。您无需通过获取控制器并调用方法来对其进行初始化,而是通过a来配置它们的创建,并通过
controllerFactory将一个
HostServices对象传递给控制器的构造函数(如果它具有合适的构造函数)来创建控制器:
public class HostServicesControllerFactory implements Callback<Class<?>,Object> { private final HostServices hostServices ; public HostServicesControllerFactory(HostServices hostServices) { this.hostServices = hostServices ; } @Override public Object call(Class<?> type) { try { for (Constructor<?> c : type.getConstructors()) { if (c.getParameterCount() == 1 && c.getParameterTypes()[0] == HostServices.class) { return c.newInstance(hostServices) ; } } return type.newInstance(); } catch (Exception e) { throw new RuntimeException(e); } }}现在,在加载FXML时使用控制器工厂:
public void start(Stage primaryStage) throws Exception { FXMLLoader loader = new FXMLLoader(getClass().getResource("main.fxml")); loader.setControllerFactory(new HostServicesControllerFactory(getHostServices())); Parent root = loader.load(); primaryStage.setScene(new Scene(root)); primaryStage.show();}并定义要
HostServices用作构造函数参数的控制器:
public class MainController { private final HostServices hostServices ; public MainController(HostServices hostServices) { this.hostServices = hostServices ; } @FXML private void showDialog() { FXMLLoader loader = new FXMLLoader(getClass().getResource("dialog.fxml")); loader.setControllerFactory(new HostServicesControllerFactory(hostServices)); Parent dialogRoot = loader.load(); Stage dialog = new Stage(); dialog.setScene(new Scene(dialogRoot)); dialog.show(); } }而且当然
public class DialogController { @FXML private Hyperlink hyperlink ; private final HostServices hostServices ; public DialogController(HostServices hostServices) { this.hostServices = hostServices ; } @FXML private void openURL() { hostServices.opendocument(hyperlink.getText()); }}解决方案3: 这是一个非常丑陋的解决方案,强烈建议您不要使用它。
我只是想包含它,所以我可以表达这一点而不会在别人发布时得罪其他人。将主机服务存储在一个静态字段中。
public class MainApp extends Application { private static HostServices hostServices ; public static HostServices getHostServices() { return hostServices ; } public void start(Stage primaryStage) throws Exception { hostServices = getHostServices(); Parent root = FXMLLoader.load(getClass().getResource("main.fxml")); primaryStage.setScene(new Scene(root)); primaryStage.show(); }}那你就做
MainApp.getHostServices().showdocument(hyperlink.getText());
您需要的任何地方。这里的问题之一是,您为需要访问主机服务的所有控制器引入了对应用程序类型的依赖。
解决方案4 定义一个单例
HostServicesProvider。这比解决方案3更好,但仍然不是imo的好解决方案。
public enum HostServicesProvider { INSTANCE ; private HostServices hostServices ; public void init(HostServices hostServices) { if (this.hostServices != null) { throw new IllegalStateException("Host services already initialized"); } this.hostServices = hostServices ; } public HostServices getHostServices() { if (hostServices == null) { throw new IllegalStateException("Host services not initialized"); } return hostServices ; }}现在你只需要
public void start(Stage primaryStage) throws Exception { HostServicesProvider.INSTANCE.init(getHostServices()); // just load and show main app...}和
public class DialogController { @FXML private Hyperlink hyperlink ; @FXML private void openURL() { HostServicesProvider.INSTANCE.getHostServices().showdocument(hyperlink.getText()); }}解决方案5 使用依赖项注入框架。这可能不适用于您当前的用例,但可能会让您了解这些(相对简单的)框架的功能。
例如,如果您使用afterburner.fx,则只需执行以下操作
Injector.setModelOrService(HostServices.class, getHostServices());
在您的应用程序
start()或
init()方法中,然后
public class DialogPresenter { @Inject private HostServices hostServices ; @FXML private Hyperlink hyperlink ; @FXML private void showURL() { hostServices.showdocument(hyperlink.getText()); }}这里有一个使用Spring的例子。



