当前位置: 首页 > 面试题库 >

JavaFx 8:在浏览器中打开链接而不引用应用程序

冯宏浚
2023-03-14
问题内容

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

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

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

但是我没有提及Application。该链接是从对话框打开的,该对话框是从控制器打开的,而该对话框是通过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: 使用控制器工厂将主机服务推送到控制器。

这是上面的更干净的版本。您无需通过获取控制器并调用方法来对其进行初始化,而是通过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的例子。



 类似资料:
  • 我在我的应用程序中启用了应用链接。它工作正常。但是在我的应用程序中,有一些情况下我无法处理传入的网址。在这种情况下,我想将该网址重定向到设备中的默认浏览器。 目前,我尝试使用intent打开带有url的浏览器,但它再次重定向到我的应用程序本身。应用程序链接的格式如下- 所以根据params,我想要么在应用程序本身中处理应用程序链接,要么将其重定向到默认浏览器。下面是我试图用上面的网址打开浏览器的代

  • 从这篇文章中,我能够创建一个功能,将用户从一个链接重定向到android或ios。但是,在检测到Android时,我想打开显示我的应用程序的游戏商店。我在重定向时尝试了以下链接: 但它会在浏览器中打开play store。我想打开play store应用程序,我假设我的应用程序用户将使用play store应用程序,所以我不想检查play store应用程序是否已安装。我也尝试了如下的市场链接 但

  • 我已将AssetLink文件托管到我们的域中https://ourdomain/.well-known/assetlinks.json并用计算机进行了验证https://developers.google.com/digital-asset-links/tools/generator通过android studio的应用程序链接Assistant,并通过两种方式获得验证状态。但当我共享调试APK进

  • 我有一个非常简单的测试应用程序,上面有一个webview组件。我正试图阻止链接在浏览器中打开,但“shouldoverrideurlloading”看起来对我不起作用。 有人能帮我看看哪里有错误吗?

  • 我最近实现了shouldInterceptRequest方法来检测链接的时间“http://sitemercado.com.br/valida“点击在android浏览器中打开它,而不是在webview中内部打开,直到它工作为止。链接在浏览器中打开,但当我回来查看webview应用程序时,它也被加载了,我希望它只在浏览器中加载。 我的代码如下: 我哪里做错了?

  • 问题内容: 是否有任何(简单/内置方式)打开新浏览器(我的意思是默认的OS浏览器)窗口来查找Electron链接的方法,而不是访问您Electron应用程序中的链接? 问题答案: 您可以简单地使用: