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.
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. –
@WolfgangFahl Tak, prawdopodobnie. Nie wiem, dlaczego próbowałbyś czegoś, czego tak zdecydowanie zalecam, aby nie używać. –
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 –