JavaFx 8: откройте ссылку в браузере без ссылки на приложение
есть гиперссылка. При нажатии я хочу, чтобы ссылка была открыта во внешнем браузере.
обычный метод, цитируемый в интернете, кажется:
final Hyperlink hyperlink = new Hyperlink("http://www.google.com");
hyperlink.setOnAction(t -> {
application.getHostServices().showDocument(hyperlink.getText());
});
однако у меня нет ссылки на Application
. Ссылка открывается из диалогового окна, которое открывается с контроллера, который открывается через файл fxml, поэтому получение ссылки на объект приложения будет довольно болезненным.
кто-нибудь знает простой способ сделать это?
Ура
3 ответов
Решение 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: использовать заводской контроллер, чтобы подтолкнуть хост-служб контроллеры.
это более чистая версия выше. Вместо того, чтобы получать контроллеры и вызывать метод для их инициализации, вы настраиваете их создание через 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
as параметр конструктора:
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 используйте структуру инъекции зависимостей. Это, вероятно, не относится к текущий вариант использования, но может дать вам представление о том, насколько мощными могут быть эти (относительно простые) фреймворки.
например, если вы используете форсаже.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 является здесь.
Если вы хотите открыть url-адрес при нажатии на кнопку внутри вашего приложения, и вы используете файл контроллера fxml, то вы можете сделать следующее...
сначала в главном файле запуска приложения получите указатель на объект HostServices и добавьте его на свой этап, например...
stage.getProperties().put("hostServices", this.getHostServices());
затем в файле контроллера fxml получите объект hostServices из объекта stage, а затем выполните метод showDocument ().
HostServices hostServices = (HostServices)this.getStage().getProperties().get("hostServices");
hostServices.showDocument("http://stackoverflow.com/");
у меня есть метод в моем класс contoller называется getStage ()...
/**
* @return the stage from my mainAnchor1 node.
*/
public Stage getStage() {
if(this.stage==null)
this.stage = (Stage) this.mainAnchor1.getScene().getWindow();
return stage;
}
другой способ-использовать java.ОУ.Столе
попробовать (непроверено):
URI uri = ...;
if (Desktop.isDesktopSupported()){
Desktop desktop = Desktop.getDesktop();
if (desktop.isSupported(Desktop.Action.BROWSE)){
desktop.browse(uri);
}
}
обратите внимание, однако, что это введет зависимость от стека AWT. Это, вероятно, не проблема, если вы работаете с полной JRE, но это может стать проблемой, если вы хотите работать с адаптированной JRE (Java SE 9 & Jigsaw) или если вы хотите запустить приложение на мобильном устройстве (javafxports).
существует открытая проблема поддержка рабочего стола в JavaFX в будущем.