I have a javafx project and I want to access the main controller by kotlin singleton reference.
But the injection cannot succeed.
A minimal example on the below:
App.kt
class App: Application() {
override fun start(primaryStage: Stage) {
val loader = FXMLLoader(javaClass.getResource("Window.fxml")).also { it.setControllerFactory { Controller } }
val root = loader.load<Parent>()
val scene = Scene(root, 400.0, 600.0)
primaryStage.scene = scene
primaryStage.show()
}
fun main(vararg args: String) {
launch(*args)
}
}
Controller.kt
object Controller : Initializable {
@FXML private lateinit var button: Button
@FXML fun buttonAction() { println("Clicked") }
override fun initialize(location: URL?, resources: ResourceBundle?) {
button.text = "Click Me"
}
}
Window.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="Controller"
prefHeight="400.0" prefWidth="600.0">
<Button fx:id="button" onAction="#buttonAction"/>
</AnchorPane>
Then I got the Exception:
Exception in Application start method
Exception in thread "main" java.lang.RuntimeException: Exception in Application start method
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:900)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: javafx.fxml.LoadException:
/D:/WorkPlace/Kotlin/LabelPlusFX/target/test-classes/window.fxml
at javafx.fxml/javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2625)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2603)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2466)
at javafx.fxml/javafx.fxml.FXMLLoader.load(FXMLLoader.java:2435)
at DemoApp.start(DemoApp.kt:16)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:846)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:455)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
... 1 more
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property button has not been initialized
at Controller.initialize(Controller.kt:20)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2573)
... 12 more
Why cannot fxml do injection successfully?
cMenuBarin the constructor, which of course is before it’s initialised. The whole premise to this question seems wrong though. It doesn’t make sense to make a JavaFX controller a singleton. - James_Dstaticfields in the Java class. TheFXMLLoaderuses reflection (of course) to initialized the@FXML-annotated fields, and only looks for instance fields (not static ones). See stackoverflow.com/questions/23105433/…. It doesn't really make sense to have static@FXML-annotated fields (for the same reason it doesn't make sense to make your controller a singleton). - James_D