2015-10-13 27 views
8

Mam hiperłącze. Po kliknięciu chcę otworzyć link w zewnętrznej przeglądarce.JavaFx 8: otwórz łącze w przeglądarce bez odniesienia do aplikacji

Zwykła metoda cytowane w sieci wydaje się być:

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

Jednak nie mam odniesienie do Application. Link otwiera się w oknie dialogowym otwieranym z kontrolera, który jest otwierany za pomocą pliku fxml, więc uzyskanie odniesienia do obiektu aplikacji byłoby dość bolesne.

Czy ktoś wie, w jaki prosty sposób to zrobić?

Cheers

Odpowiedz

14

Rozwiązanie 1: Przekaż odwołanie do HostServices za pomocą aplikacji.

Jest to prawdopodobnie podobne do podejścia "dość bolesnego", którego się spodziewasz. Ale w zasadzie byłoby zrobić coś takiego:

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

} 

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

i oczywiście DialogController wygląda następująco:

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

Rozwiązanie 2: użytku fabryka kontrolerów do przepchnięcia serwera hosta es do kontrolerów.

To jest czystsza wersja powyższego.Zamiast się kontrolerów i wywołanie metody, aby je zainicjować, skonfigurować tworzenie z nich za pośrednictwem controllerFactory i tworzenia kontrolerów przepuszczając HostServices obiektu do konstruktora sterownika, jeżeli posiada odpowiedni konstruktor:

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

teraz skorzystać z fabryki kontrolera podczas ładowania 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(); 
} 

i zdefiniować swoje kontrolery do podjęcia HostServices jako parametr konstruktora:

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

i oczywiście

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

Rozwiązanie 3:Jest to marnie brzydki rozwiązanie, a ja zdecydowanie odradzam korzystania z niej. Po prostu chciałem to włączyć, abym mógł wyrazić to bez obrażania kogoś, kto to opublikował. Przechowuj usługi hosta w polu statycznym.

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

Wtedy po prostu zrobić

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

gdziekolwiek chcesz. Jednym z problemów jest wprowadzenie zależności od typu aplikacji dla wszystkich kontrolerów, które potrzebują dostępu do usług hosta.


Rozwiązanie 4 Definiowanie pojedyncza HostServicesProvider. To jest lepsze niż rozwiązanie 3, ale nadal nie jest dobrym rozwiązaniem.

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

Teraz wystarczy

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

i

public class DialogController { 

    @FXML 
    private Hyperlink hyperlink ; 

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

Rozwiązanie 5 pomocą ramy iniekcji zależność. Prawdopodobnie nie dotyczy to Twojego obecnego przypadku użycia, ale może dać ci wyobrażenie o tym, jak potężne mogą być te (względnie proste) ramy.

Na przykład, jeśli używasz afterburner.fx, po prostu trzeba zrobić

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

w aplikacji start() lub init() metody, a następnie

public class DialogPresenter { 

    @Inject 
    private HostServices hostServices ; 

    @FXML 
    private Hyperlink hyperlink ; 

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

Przykładem korzystania sprężyna jest here.

+0

brzydkie rozwiązanie statyczne nie będzie działać, jeśli nie nazwać inaczej np getHostServices getStaticHostServices. Jest to najmniej kosztowne rozwiązanie w moim przypadku użycia. –

+0

@WolfgangFahl Tak, prawdopodobnie. Nie wiem, dlaczego próbowałbyś czegoś, czego tak zdecydowanie zalecam, aby nie używać. –

+0

Bez obaw Korzystam z interfejsu teraz Interfejs publiczny Linker { publiczny przeglądanie void (URL String); } który ukrywa implementację - statyczne rzeczy nie są potrzebne w ten sposób –

1

Innym sposobem byłoby wykorzystanie java.awt.Desktop

Try (niesprawdzone):

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

Należy jednak pamiętać, że ta zależność będzie wprowadzenie do stosu AWT. Prawdopodobnie nie jest to problemem, jeśli pracujesz z pełnym środowiskiem JRE, ale może to być problem, jeśli chcesz pracować z dostosowanym JRE (Java SE 9 & Jigsaw) lub jeśli chcesz uruchomić aplikację na urządzeniu mobilnym (javafxports).

Istnieje otwarty numer do support Desktop in JavaFX w przyszłości.

3

Jeśli chcesz otworzyć adres URL po kliknięciu przycisku wewnątrz aplikacji i korzystasz z pliku kontrolera fxml, możesz wykonać następujące czynności ...

pierwsze w swojej głównej aplikacji plik startowy uzyskać wskaźnik do HostServices sprzeciw i dodać go do etapu, takich jak ...

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

Następnie w pliku sterownika fxml uzyskać hostServices sprzeciw od obiektu Stage a następnie wykonaj metodę showDocument().

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

Mam metodę w moim klasy contoller zwanego 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

co to jest mainAnchor1? – simpleuser

Powiązane problemy