Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
JavaFx - Spring DependencyInjection with fx:include
Ich hab ein main.fxml wo mittels <fx:include source="menubar.fxml" /> die Menübar reingeladen wird.
Diese menubar hat einen eigenen Controller. Doch leider kann ich diese Controller mit Spring nicht injecten.
Durch fx:include wird scheinbar eine neue Instanz von dem Controller gemacht.
Ich lehne mich vielleicht aus dem Fenster, aber im "normalen" JavaFX, glaube ich, gibt es zwe Wege, den Controller festzulegen. Der erste Weg ist über das FXML (das finden einige doof, da man der Deklaration sagt, was sein Controller ist - viele finden den Weg anders herum besser: Hab deinen Controller und sag ihm, was die UI ist). Der zweite weg ist über den FXMLLoader. Wenn du eine neue Instanz anlegst (nicht die statische Methode verwendest), kannst du dieser einen Controller für ein FXML zuweisen - hier könntest du nun vielleicht mit deiner Spring-DI arbeiten.
So. Habs nun gelöst. Für alle welche das selbe Problem haben:
Es gibt das main.fxml welche das menubar.fxml inclucded. Damit dann im MenubarController mittel Spring-Autowire beans injected werden können, muss dem FXMLLoader ein ControllerFactory gesetzt werden.
Start des Programms:
Java:
private AnnotationConfigApplicationContext context;
@Override
public void stop() throws Exception
{
if (context != null)
{
context.close();
}
super.stop();
}
@Override
public void start(Stage primaryStage) throws Exception
{
context = new AnnotationConfigApplicationContext(SpringFactory.class);
MainController mainController = context.getBean(MainController.class);
mainController.init(primaryStage, context);
mainController.showMainView();
}
public static void main(String[] args)
{
launch(args);
}
Methode in mainController:
Java:
public void init(Stage primaryStage, final ApplicationContext context)
{
try
{
primaryStage.getIcons().add(new Image("file:resources/images/windowIcon.jpg"));
primaryStage.setTitle("...");
InputStream fxmlStream = null;
try
{
fxmlStream = getClass().getResourceAsStream(StaticConfig.FXML_MAIN);
FXMLLoader loader = new FXMLLoader(getClass().getResource(StaticConfig.FXML_FOLDER));
loader.setControllerFactory(new Callback<Class<?>, Object>()
{
@Override
public Object call(Class<?> clazz)
{
return context.getBean(clazz);
}
});
loader.load(fxmlStream);
Scene myScene = new Scene(((MainController)loader.getController()).getView());
primaryStage.setScene(myScene);
}
finally
{
if (fxmlStream != null)
{
fxmlStream.close();
}
}
}
catch (Exception e)
{
Dialogs.showErrorDialog(primaryStage,
"Coult not load main Fxml: " + StaticConfig.FXML_MAIN,
"MainView", "Error", e);
}
}
1)
Ok, anscheinend war es nicht deutlich genug, aber deine Lösung geht haargenau in die Richtung, die ich auch vorgeschlagen habe.
2)
Code:
FXMLLoader loader = new FXMLLoader(getClass().getResource(StaticConfig.FXML_FOLDER));
==
Code:
FXMLLoader loader = new FXMLLoader(); loader.load(getClass().getResource("layout/Configuration.fxml").openStream());
==
Code:
FXMLLoader loader = new FXMLLoader(); loader.load(getClass().getResourceAsStream("layout/Configuration.fxml"));
==
Code:
FXMLLoader loader = new FXMLLoader(); loader.load(fxmlStream);
Du kannst dich dafür entscheiden, die FXML entweder im Konstruktor, oder über load anzugeben. Was du machst ist "doppelt gemoppelt" - sozusagen. Nicht schlimm, aber auch nicht notwendig.
3)
Ich kenne deine FXML nicht, ich vermute aber, dass dein Controller im FXML selbst steht, also irgend etwas wie
Code:
fx:controller="my.package.MainController"
. Dadurch kannst du dich mit deiner ControllerFactory dazwischen hängen (bzw. musst es auf diese Art).
Ich habe vorgeschlagen, dass du die deklarative UI in Unkenntnis über ihren späteren Controller lässt (der Weg, wie ihn z.B. Android geht: Dort wird die Actity oder ein Fragment so aufgebaut, dass es die deklarative UI (auch in XML-Form) lädt - das XML aber kennt die Activity nicht vorab).
Wenn du also den
Code:
fx:controller="my.package.MainController"
-Teil weg lässt, kannst du im FXMLoader stattdessen einfach
Ja, war verständlicher.
Es löst aber mein Hauptproblem nicht.
Wenn ich es wie du mache und den Controller im FXML weg lasse, ihn dann mittels loader.setController(context.getBean(MainController.class)); setze, wie kann ich dem 2ten-FXML (welches im mainFXML included wird) einen Controller zuweisen?
Ah! Ich sehe, woher der Wind weht. Ja du hast recht, daran habe ich nicht gedacht. Und da ich es noch nie auf diese Art probiert hab...
Wenn ich so eine Art von UI-Dependency (kann man das überhaupt so nennen???) habe, dann bau ich das immer im Controller auf. Also in deinem Fall würde im Controller der Hauptseite, das FXML des Menüs laden und einfügen. Ich behaupte dabei aber nicht, dass das der bessere Weg wäre.
Was ich mich nur aus purer Neugier frage, ist: Wenn du keinen Controller angibst, kannst du dann die @FXML-Annotationen einfach im Controller der Hauptseite machen? Vielleicht ginge das ja. Dann hättest du halt einen Controller für zwei UIs (ob das dann allerdings noch im Sinne des Erfinders ist, weiß ich wiederum auch nicht).