JavaFx 8:在浏览器中打开链接而不引用Application [英] JavaFx 8: open a link in a browser without reference to Application

查看:160
本文介绍了JavaFx 8:在浏览器中打开链接而不引用Application的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Have got a Hyperlink. When clicked I want a link to be opened in an external browser.

网上引用的常用方法似乎是:

The usual method cited on the web seems to be:

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

但是我没有引用 Application 。该链接是从Dialog打开的,它是通过一个fxml文件打开的Controller打开的,所以获取对Application对象的引用会非常痛苦。

However I don't have a reference to Application. The link is opened from Dialog, which is opened from a Controller, which is opened via an fxml file, so getting a reference to the Application object would be quite painful.

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

Does anyone know an easy way of doing this?

干杯

推荐答案

解决方案1:在您的应用程序中向下传递 HostServices 的引用。

Solution 1: Pass a reference to the HostServices down through your application.

这可能类似于您预期的非常痛苦的方法。但基本上你会做类似的事情:

This is probably similar to the "quite painful" approach you are anticipating. But basically you would do something like:

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 :使用控制器工厂将主机服务推送到控制器。


Solution 2: Use a controller factory to push the host services to the controllers.

这是上面的清洁版本。您可以通过 controllerFactory 并通过传递 HostServices 来创建控制器对象到控制器的构造函数,如果它有一个合适的构造函数:

This is a cleaner version of the above. Instead of getting the controllers and calling a method to initialize them, you configure the creation of them via a controllerFactory and create controllers by passing a HostServices object to the controller's constructor, if it has a suitable constructor:

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:

Now use the controller factory when you load the 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 作为构造函数参数:

and define your controllers to take HostServices as a constructor parameter:

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: 这是一个非常丑陋的解决方案,我强烈建议不要使用它。我只是想要包含它以便我可以表达这一点,而不会在发布时冒犯别人它。将主机服务存储在静态字段中。


Solution 3: This is a miserably ugly solution, and I strongly recommend against using it. I just wanted to include it so I could express that without offending someone else when they posted it. Store the host services in a static field.

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

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

anywhere you need. One of the problems here is that you introduce a dependency on your application type for all controllers that need access to the host services.

解决方案4 定义单件 HostServicesProvider 。这比解决方案3更好,但仍然不是一个好的解决方案。

Solution 4 Define a singleton HostServicesProvider. This is better than solution 3, but still not a good solution 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 使用依赖注入框架。这可能不适用于您当前的用例,但可能会让您了解这些(相对简单的)框架的强大程度。


Solution 5 Use a dependency injection framework. This probably doesn't apply to your current use case, but might give you an idea how powerful these (relatively simple) frameworks can be.

例如,如果您是使用 afterburner.fx ,你只需要做

For example, if you are using afterburner.fx, you just need to do

Injector.setModelOrService(HostServices.class, getHostServices());

在您的应用程序中 start() init()方法,然后

in your application start() or init() method, and then

public class DialogPresenter {

    @Inject
    private HostServices hostServices ;

    @FXML
    private Hyperlink hyperlink ;

    @FXML
    private void showURL() {
        hostServices.showDocument(hyperlink.getText());
    }
}

使用Spring的一个例子是此处

An example using Spring is here.

这篇关于JavaFx 8:在浏览器中打开链接而不引用Application的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆