JavaFX8:在浏览器中打开链接而不引用应用程序
我有一个超链接。单击时,我希望在外部浏览器中打开链接 网络上引用的常用方法似乎是:JavaFX8:在浏览器中打开链接而不引用应用程序,java,javafx,javafx-8,Java,Javafx,Javafx 8,我有一个超链接。单击时,我希望在外部浏览器中打开链接 网络上引用的常用方法似乎是: final Hyperlink hyperlink = new Hyperlink("http://www.google.com"); hyperlink.setOnAction(t -> { application.getHostServices().showDocument(hyperlink.getText()); }); 但是,我没有对应用程序的引用。链接是从对话框打开的,对话框是从控制器
final Hyperlink hyperlink = new Hyperlink("http://www.google.com");
hyperlink.setOnAction(t -> {
application.getHostServices().showDocument(hyperlink.getText());
});
但是,我没有对应用程序的引用。链接是从对话框打开的,对话框是从控制器打开的,控制器是通过fxml文件打开的,因此获取对应用程序对象的引用将非常痛苦
有人知道一个简单的方法吗
干杯另一种方法是使用
尝试(未经测试):
但是请注意,这将向AWT堆栈引入依赖项。如果您使用的是完整的JRE,这可能不是问题,但是如果您想要使用定制的JRE(JavaSE9&Jigsaw)或者想要在移动设备上运行应用程序(),这可能会成为问题
将来还有一个有待解决的问题。解决方案1:通过应用程序向下传递对主机服务的引用
这可能与您预期的“相当痛苦”的方法类似。但基本上你会做一些类似的事情:
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();
}
然后在main控制器中
:
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:使用控制器工厂将主机服务推送到控制器
这是上述内容的更清晰版本。您不需要获取控制器并调用方法对其进行初始化,而是通过配置来创建控制器,并通过将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);
}
}
}
并将控制器定义为将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...
}
及
解决方案5使用依赖项注入框架。这可能不适用于您当前的用例,但可能会让您了解这些(相对简单的)框架有多强大
例如,如果您正在使用,您只需要
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的一个示例是。如果您想在单击应用程序内的按钮时打开url,并且您正在使用fxml控制器文件,则可以执行以下操作
首先,在主应用程序启动文件中,获取指向HostServices对象的指针,并将其添加到后台,例如
stage.getProperties().put("hostServices", this.getHostServices());
然后在fxml控制器文件中,从stage对象获取hostServices对象,然后执行showDocument()方法
在我的控件类中有一个名为getStage()的方法
什么是mainAnchor1?我喜欢这一种方式,而不是这里的任何其他答案。如果不以不同的方式命名getHostServices(例如getStaticHostServices),丑陋的静态解决方案将不起作用。在我的用例中,这是最省力的解决方案。@WolfgangFahl是的,可能吧。我不知道你为什么会尝试我强烈建议你不要使用的东西。不用担心,我现在使用的是一个接口公共接口链接器{public void browse(String url);},它隐藏了实现-静态的东西不需要这样做:)谢谢
Injector.setModelOrService(HostServices.class, getHostServices());
public class DialogPresenter {
@Inject
private HostServices hostServices ;
@FXML
private Hyperlink hyperlink ;
@FXML
private void showURL() {
hostServices.showDocument(hyperlink.getText());
}
}
stage.getProperties().put("hostServices", this.getHostServices());
HostServices hostServices = (HostServices)this.getStage().getProperties().get("hostServices");
hostServices.showDocument("http://stackoverflow.com/");
/**
* @return the stage from my mainAnchor1 node.
*/
public Stage getStage() {
if(this.stage==null)
this.stage = (Stage) this.mainAnchor1.getScene().getWindow();
return stage;
}