2015-10-13 116 views
8

已獲得超鏈接。點擊時,我想要在外部瀏覽器中打開鏈接。JavaFx 8:在瀏覽器中打開鏈接而不參考應用程序

在網絡上引用的常用方法似乎是:

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

不過,我沒有提到Application。從對話框打開該鏈接,該對話框是通過一個通過fxml文件打開的控制器打開的,因此獲取對該應用程序對象的引用將非常痛苦。

有沒有人知道這樣做的簡單方法?

乾杯

回答

14

解決方案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:使用一個控制器工廠推動主機服務es到控制器。

這是上述清潔版本。相反,越來越控制器並調用一個方法來初始化它們,你通過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更好,但仍不是一個好的解決方案。

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的一個例子是here

+0

如果你不命名getHostServices例如不同的醜惡靜態解決方案將無法正常工作getStaticHostServices。這是我用例中最省力的解決方案。 –

+0

@WolfgangFahl是的,可能。不知道爲什麼你甚至會嘗試我強烈建議不要使用的東西。 –

+0

不用擔心我現在正在使用一個接口 public interface Linker {0} {0} {0} public void browse(String url); } 其中隱藏了實現 - 靜態東西不需要那種方式 –

1

另一種方法是使用java.awt.Desktop

嘗試(未經測試):

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

不過請注意,這將引入一個依賴於AWT堆棧。如果您使用完整的JRE,這可能不是問題,但如果您想要使用定製的JRE(Java SE 9 &拼圖)或者想要在移動設備上運行應用程序( javafxports)。

未來有一個未解決的問題support Desktop in JavaFX

3

如果你想打開一個url,當你點擊你的應用程序中的一個按鈕,並且你正在使用一個fxml控制器文件,那麼你可以執行以下操作...

首先在主應用程序啓動文件得到一個指向對象HostServices並將其添加到您的階段,如...

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

然後在您FXML控制器文件獲取hostServices從舞臺Object對象然後執行showDocument()方法。

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

我有一個方法叫我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

什麼是mainAnchor1? – simpleuser

相關問題