Soluzione 1: Passare un riferimento allo HostServices
attraverso l'applicazione.
Questo è probabilmente simile all'approccio "abbastanza doloroso" che si sta anticipando. Ma in fondo si potrebbe fare qualcosa di simile:
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();
}
e poi 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();
}
}
e naturalmente DialogController
assomiglia:
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());
}
}
Soluzione 2: Usa una fabbrica di controller per inviare il servizio host es ai controller.
Questa è una versione più pulita di quanto sopra.Invece di ottenere i controllori e chiamando un metodo per inizializzare loro, si configurare la creazione di loro tramite un controllerFactory
e creare controller passando un oggetto HostServices
al costruttore del controller, se si ha un costruttore adeguato:
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);
}
}
}
Ora utilizzare la fabbrica di controllo quando si carica il 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();
}
e definire i controller di prendere HostServices
come parametro del costruttore:
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();
}
}
e naturalmente
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());
}
}
Soluzione 3:questa è una soluzione miseramente brutto, e vi consiglio vivamente l'uso di esso. Volevo solo includerlo in modo da poterlo esprimere senza offendere qualcun altro quando lo hanno pubblicato. Archiviare i servizi host in un campo statico.
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();
}
}
Poi basta fare
MainApp.getHostServices().showDocument(hyperlink.getText());
ovunque sia necessario. Uno dei problemi qui è che si introduce una dipendenza dal tipo di applicazione per tutti i controller che devono accedere ai servizi host.
Soluzione 4 Definire un singleton HostServicesProvider
. Questo è meglio della soluzione 3, ma non è ancora una buona soluzione.
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 ;
}
}
Ora non vi resta
public void start(Stage primaryStage) throws Exception {
HostServicesProvider.INSTANCE.init(getHostServices());
// just load and show main app...
}
e
public class DialogController {
@FXML
private Hyperlink hyperlink ;
@FXML
private void openURL() {
HostServicesProvider.INSTANCE.getHostServices().showDocument(hyperlink.getText());
}
}
Soluzione 5 utilizzare un quadro di iniezione di dipendenza. Questo probabilmente non si applica al tuo caso d'uso corrente, ma potrebbe darti un'idea di quanto potenti possano essere questi framework (relativamente semplici).
Ad esempio, se si utilizza afterburner.fx, basta fare
Injector.setModelOrService(HostServices.class, getHostServices());
nell'applicazione start()
o init()
metodo, e quindi
public class DialogPresenter {
@Inject
private HostServices hostServices ;
@FXML
private Hyperlink hyperlink ;
@FXML
private void showURL() {
hostServices.showDocument(hyperlink.getText());
}
}
Un esempio utilizzando la primavera è here.
la soluzione statica brutto non funziona se non chiami i getHostServices modo diverso per esempio getStaticHostServices. È la soluzione meno faticosa nel mio caso d'uso. –
@WolfgangFahl Sì, probabilmente. Non sono sicuro del perché potresti provare qualcosa che consiglio vivamente di non usare comunque. –
nessuna preoccupazione Sto usando un'interfaccia ora interfaccia pubblica Linker { public void browse (String url); } che nasconde l'implementazione - le cose statiche non sono necessarie in questo modo –