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

Ben*_*Ben 11 java javafx javafx-8

有一个超链接.单击时,我想要在外部浏览器中打开一个链接.

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

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

但是我没有参考Application.所述链路是从对话,这是从一个控制器,其经由FXML文件打开打开打开,从而获得到应用程序对象的引用将是相当痛苦.

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

干杯

Jam*_*s_D 22

解决方案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();

}
Run Code Online (Sandbox Code Playgroud)

然后在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();
    }
}
Run Code Online (Sandbox Code Playgroud)

当然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());
    }
}
Run Code Online (Sandbox Code Playgroud)

解决方案2:使用控制器工厂将主机服务推送到控制器.

这是上面的清洁版.您可以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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在在加载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();
}
Run Code Online (Sandbox Code Playgroud)

并定义您的控制器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();
    }    
}
Run Code Online (Sandbox Code Playgroud)

而且当然

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

解决方案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();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你就做了

MainApp.getHostServices().showDocument(hyperlink.getText());
Run Code Online (Sandbox Code Playgroud)

随时随地.这里的一个问题是,您需要为需要访问主机服务的所有控制器引入应用程序类型的依赖关系.


解决方案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 ;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你需要

public void start(Stage primaryStage) throws Exception {
    HostServicesProvider.INSTANCE.init(getHostServices());
    // just load and show main app...
}
Run Code Online (Sandbox Code Playgroud)

public class DialogController {

    @FXML
    private Hyperlink hyperlink ;

    @FXML
    private void openURL() {
        HostServicesProvider.INSTANCE.getHostServices().showDocument(hyperlink.getText());
    }
}
Run Code Online (Sandbox Code Playgroud)

解决方案5使用依赖注入框架.这可能不适用于您当前的用例,但可能会让您了解这些(相对简单的)框架有多强大.

例如,如果您使用的是afterburner.fx,则只需要这样做

Injector.setModelOrService(HostServices.class, getHostServices());
Run Code Online (Sandbox Code Playgroud)

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

public class DialogPresenter {

    @Inject
    private HostServices hostServices ;

    @FXML
    private Hyperlink hyperlink ;

    @FXML
    private void showURL() {
        hostServices.showDocument(hyperlink.getText());
    }
}
Run Code Online (Sandbox Code Playgroud)

这里使用Spring的一个例子.


Pet*_*ter 6

如果您想在单击应用程序内的按钮时打开URL,并且您正在使用fxml控制器文件,那么您可以执行以下操作...

首先在您的主应用程序启动文件中获取指向HostServices对象的指针并将其添加到您的舞台,例如...

stage.getProperties().put("hostServices", this.getHostServices());
Run Code Online (Sandbox Code Playgroud)

然后在fxml控制器文件中从stage对象获取hostServices对象,然后执行showDocument()方法.

HostServices hostServices = (HostServices)this.getStage().getProperties().get("hostServices");
hostServices.showDocument("http://stackoverflow.com/");
Run Code Online (Sandbox Code Playgroud)

我的控制器类中有一个名为getStage()的方法...

/**
 * @return the stage from my mainAnchor1 node.
 */
public Stage getStage() {
    if(this.stage==null)
        this.stage = (Stage) this.mainAnchor1.getScene().getWindow();
    return stage;
}
Run Code Online (Sandbox Code Playgroud)