2015-10-13 10 views
8

Haben Sie einen Hyperlink. Wenn ich darauf klicke, möchte ich, dass ein Link in einem externen Browser geöffnet wird.JavaFx 8: Öffnen Sie einen Link in einem Browser ohne Bezug auf Anwendung

Die übliche Methode, die im Internet zitiert zu sein scheint:

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

aber ich habe nicht einen Verweis auf Application. Die Verknüpfung wird von Dialog aus geöffnet, der von einem Controller aus geöffnet wird, der über eine fxml-Datei geöffnet wird. Daher wäre es sehr schmerzhaft, einen Verweis auf das Application-Objekt zu erhalten.

Kennt jemand eine einfache Möglichkeit, dies zu tun?

URI uri = ...; 
if (Desktop.isDesktopSupported()){ 
    Desktop desktop = Desktop.getDesktop(); 
    if (desktop.isSupported(Desktop.Action.BROWSE)){ 
     desktop.browse(uri); 
    } 
} 

ist jedoch zu beachten, dass dies eine Abhängigkeit des AWT-Stack einführen:

Prost

Antwort

14

Lösung 1: Übergeben Sie einen Verweis auf die HostServices nach unten durch Ihre Anwendung.

Dies ist wahrscheinlich ähnlich dem "ziemlich schmerzhaften" Ansatz, den Sie erwarten. Aber im Grunde würden Sie so etwas wie:

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

} 

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

und natürlich DialogController wie folgt aussieht:

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

Lösung 2: Verwenden eine Controller-Fabrik, um den Host-Dienst zu drücken es zu den Controllern.

Dies ist eine sauberere Version des oben genannten.Statt die Controller des Erhaltens und Aufrufen einer Methode, sie zu initialisieren, konfigurieren Sie die Erstellung von ihnen über einen controllerFactory und Controller erstellen, indem ein HostServices Objekt an den Konstruktor der Controller vorbei, wenn es einen geeigneten Konstruktor hat:

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

Jetzt verwenden Sie die Controller Factory, wenn Sie die FXML laden:

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

und definieren Sie Ihre Controller HostServices als Konstrukteur Parameter zu übernehmen:

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

und natürlich

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

Lösung 3:Dies ist eine kläglich hässliche Lösung, und ich gegen die Verwendung es nur sehr empfehlen. Ich wollte es nur hinzufügen, damit ich das ausdrücken konnte, ohne jemand anderen zu beleidigen, wenn sie es gepostet haben. Speichern Sie die Host-Services in einem statischen Feld.

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

Dann tun Sie gerade

MainApp.getHostServices().showDocument(hyperlink.getText()); 

überall Sie benötigen. Eines der Probleme besteht darin, dass Sie für alle Controller, die Zugriff auf die Host-Services benötigen, eine Abhängigkeit von Ihrem Anwendungstyp einführen.


Lösung 4 Definieren eines Singletons HostServicesProvider. Das ist besser als Lösung 3, aber immer noch keine gute Lösung 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 ; 
    } 
} 

Jetzt müssen Sie nur

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

und

public class DialogController { 

    @FXML 
    private Hyperlink hyperlink ; 

    @FXML 
    private void openURL() { 
     HostServicesProvider.INSTANCE.getHostServices().showDocument(hyperlink.getText()); 
    } 
} 

Lösung 5 ein Dependency Injection-Framework verwenden. Dies trifft wahrscheinlich nicht auf Ihren aktuellen Anwendungsfall zu, könnte Ihnen aber eine Vorstellung davon geben, wie mächtig diese (relativ einfachen) Frameworks sein können.

Zum Beispiel, wenn Sie afterburner.fx verwenden, müssen Sie nur

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

in Ihrer Anwendung start() oder init() Methode zu tun, und dann

public class DialogPresenter { 

    @Inject 
    private HostServices hostServices ; 

    @FXML 
    private Hyperlink hyperlink ; 

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

Ein Beispiel Frühling ist mit here.

+0

die hässliche statische Lösung wird nicht funktionieren, wenn Sie das getHostServices nicht anders nennen z.B. getStaticHostServices. Es ist die Lösung mit dem geringsten Aufwand in meinem Anwendungsfall. –

+0

@WolfgangFahl Ja, wahrscheinlich. Ich bin mir nicht sicher, warum Sie überhaupt etwas versuchen sollten, das ich so dringend davon abraten kann, es trotzdem zu benutzen. –

+0

keine Sorgen, ich benutze jetzt eine Schnittstelle öffentliche Schnittstelle Linker { öffentlichen leeren Durchsuchen (String URL); } was verbirgt die Implementierung - das statische Zeug ist nicht auf diese Weise benötigt –

1

Ein anderer Weg java.awt.Desktop

Try (ungetestet) zu verwenden wäre. Dies ist wahrscheinlich kein Problem, wenn Sie mit der vollständigen JRE arbeiten, aber es könnte ein Problem werden, wenn Sie mit einer maßgeschneiderten JRE (Java SE 9 Jigsaw) arbeiten möchten oder wenn Sie Ihre Anwendung auf einem mobilen Gerät ausführen möchten (javafxports).

In der Zukunft gibt es ein offenes Problem zu support Desktop in JavaFX.

3

Wenn Sie eine URL öffnen möchten, wenn Sie auf eine Schaltfläche in Ihrer App klicken und eine fxml-Controller-Datei verwenden, können Sie Folgendes tun ...

Erste in Ihrer Hauptanwendung Startup-Datei erhält einen Zeiger auf den HostServices Objekt und fügen Sie es zu Ihrer Bühne wie ...

stage.getProperties().put("hostServices", this.getHostServices()); 

Dann in Ihrer fxml Controller Datei die hostServices von der Bühne Objekt Objekt erhalten und führen Sie dann die showDocument() -Methode aus.

HostServices hostServices = (HostServices)this.getStage().getProperties().get("hostServices"); 
hostServices.showDocument("http://stackoverflow.com/"); 

Ich habe eine Methode in meiner contoller Klasse namens getStage() ...

/** 
* @return the stage from my mainAnchor1 node. 
*/ 
public Stage getStage() { 
    if(this.stage==null) 
     this.stage = (Stage) this.mainAnchor1.getScene().getWindow(); 
    return stage; 
} 
+0

Was ist MainAnchor1? – simpleuser

Verwandte Themen