JavaFx 8 : open a link in a browser without reference to Application

标签 java javafx javafx-8

有超链接。单击时,我希望在外部浏览器中打开一个链接。

网上引用的常用方法好像是:

final Hyperlink hyperlink = new Hyperlink("http://www.google.com");
hyperlink.setOnAction(t -> {
    application.getHostServices().showDocument(hyperlink.getText());
});

但是我没有对 Application 的引用。链接是从 Dialog 打开的,而 Dialog 是从 Controller 打开的,而 Controller 通过 fxml 文件打开,因此获取对 Application 对象的引用会非常痛苦。

有人知道这样做的简单方法吗?

干杯

最佳答案

解决方案 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:使用 Controller 工厂将主机服务推送到 Controller 。

这是上面的一个更简洁的版本。您无需获取 Controller 并调用方法来初始化它们,而是通过 controllerFactory 配置它们的创建。并通过将 HostServices 对象传递给 Controller ​​的构造函数来创建 Controller ,如果它有合适的构造函数的话:

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 时使用 Controller 工厂:

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();
}

并定义您的 Controller 以将 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());

任何你需要的地方。这里的问题之一是您为需要访问主机服务的所有 Controller 引入了对应用程序类型的依赖。


解决方案 4 定义一个单例 HostServicesProvider。这比解决方案 3 好,但在我看来仍然不是一个好的解决方案。

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 使用依赖注入(inject)框架。这可能不适用于您当前的用例,但可能会让您了解这些(相对简单的)框架有多么强大。

例如,如果您使用 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 的例子是 here .

关于JavaFx 8 : open a link in a browser without reference to Application,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33094981/

相关文章:

java - 如何使用 NetBeans 计算或确定用 Java 编写的程序的内存消耗?

java - onScroll 监听器在 JavaFX 2 的 TableView 中不起作用

JavaFX 用更少的代码检测按钮点击

java - Gluon Project中如何引用android.jar

java - 使用 JavaFX 8 播放 Youtube-LiveStreams

java - 在 AsyncTask 中同步两个并发网络调用

java - 我可以选择我在 Maven 依赖项中使用的唯一包来在 Eclipse 中导出可运行的 jar 吗?

java - 从数据库添加菜单按钮

javafx-8 - JavaFX 8,如何获取滚动 Pane 视口(viewport)的中心位置

java - spring mvc 与 hibernate 一起使用更好吗?