web-dev-qa-db-ja.com

JavaFx 8:アプリケーションを参照せずにブラウザでリンクを開く

ハイパーリンクを持っています。クリックすると、外部ブラウザでリンクを開くようにします。

ウェブ上で引用されている通常の方法は次のようです。

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

ただし、Applicationへの参照はありません。リンクは、fxmlファイルを介して開かれるコントローラーから開かれるDialogから開かれるため、Applicationオブジェクトへの参照を取得するのは非常に困難です。

誰かがこれを行う簡単な方法を知っていますか?

乾杯

11
Ben

解決策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をコンストラクターパラメーターとして受け取るようにコントローラーを定義します。

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

必要な場所ならどこでも。ここでの問題の1つは、ホストサービスへのアクセスを必要とするすべてのコントローラーのアプリケーションタイプへの依存関係を導入することです。


ソリューション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依存性注入フレームワークを使用します。これはおそらく現在のユースケースには当てはまりませんが、これらの(比較的単純な)フレームワークがどれほど強力であるかを理解できるかもしれません。

たとえば、 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を使用した例は ここ です。

23
James_D

アプリ内のボタンをクリックしたときにURLを開き、fxmlコントローラーファイルを使用している場合は、次の操作を実行できます...

最初にメインアプリケーションのスタートアップファイルでHostServicesオブジェクトへのポインタを取得し、それを次のようなステージに追加します...

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

次に、fxmlコントローラーファイルで、ステージオブジェクトからhostServicesオブジェクトを取得し、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;
}
6
Peter

別の方法は、 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およびJigsaw)を使用している場合、またはモバイルデバイスでアプリケーションを実行している場合( javafxports )。

将来的には JavaFXでデスクトップをサポート に未解決の問題があります。

2
Puce