使用自定义 JavaFXBuilderFactory 时如何修复 'IllegalStateException'
How to fix 'IllegalStateException' when using custom JavaFXBuilderFactory
首先,我在 JavaFX 项目中使用 Kotlin。
我正在尝试实现我自己的扩展 Canvas
.
的 JavaFX 节点
class BrikkCanvas(width: Double, height: Double, private val rows: Int, private val cols: Int) : Canvas(width, height)
我也希望能够像这样直接在 FXML 文件中添加 BrikkCanvas
<BrikkCanvas fx:id="myCanvas" width="100.0" height="100.0" rows="1" cols="1" />
我的 class 没有默认构造函数,这就是为什么将它包含在 FXML 中并非易事。
不过,我发现您可以实现自定义 BuilderFactory
,所以我做到了:
class BrikkCanvasBuilderFactory : BuilderFactory {
private val defaultBuilderFactory = JavaFXBuilderFactory()
override fun getBuilder(clazz: Class<*>): Builder<*> =
if (clazz == BrikkCanvas::class.java) BrikkCanvasBuilder()
else defaultBuilderFactory.getBuilder(clazz)
private class BrikkCanvasBuilder : Builder<BrikkCanvas> {
var width: Double = 0.0
var height: Double = 0.0
var rows: Int = 0
var cols: Int = 0
override fun build(): BrikkCanvas = BrikkCanvas(width, height, rows, cols)
}
}
在扩展 Application
的 App
class 中,我这样使用 BrikkCanvasBuilderFactory
:
fun startTheGame(playerName: String) {
val loader = FXMLLoader(App::class.java.getResource("game.fxml"), null, BrikkCanvasBuilderFactory())
val scene = Scene(loader.load())
val controller = loader.getController<GameController>()
primaryStage.scene = scene
}
但是,当我启动应用程序并单击调用 startTheGame
的按钮时,出现以下错误:
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
...
Caused by: java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
...
Caused by: javafx.fxml.LoadException:
/path to project/target/classes/game.fxml:21
at App$Companion.startTheGame(App.kt:18)
...
Caused by: java.lang.IllegalStateException: defaultBuilderFactory.getBuilder(clazz) must not be null
at controller.BrikkCanvasBuilderFactory.getBuilder(BrikkCanvasBuilderFactory.kt:12)
game.fxml:21
= fx:controller="controller.GameController"
App$Companion.startTheGame(App.kt:18)
= val scene = Scene(loader.load())
BrikkCanvasBuilderFactory.kt:12
= else defaultBuilderFactory.getBuilder(clazz)
请注意,我什至还没有在 FXML 文件中包含任何 <BrikkCanvas ... />
标签
我很确定我遗漏了一些非常基本的东西,这可能与我使用 Kotlin 而不是 Java
这一事实有关
据我了解,BuilderFactory
和 Builder
接口是过去的遗物(基本上已弃用),应在新代码中避免使用——假装它们不存在。您应该改为使用 @NamedArg
注释来注释您的构造函数参数。
class BrikkCanvas(
@NamedArg("width") width: Double,
@NamedArg("height") height: Double,
@param:NamedArg("rows") private val rows: Int,
@param:NamedArg("cols") private val cols: Int
) : Canvas(width, height)
然后 FXMLLoader
将能够分辨出哪个 FXML 属性对应于哪个参数,从而可以通过构造函数设置属性。
首先,我在 JavaFX 项目中使用 Kotlin。
我正在尝试实现我自己的扩展 Canvas
.
class BrikkCanvas(width: Double, height: Double, private val rows: Int, private val cols: Int) : Canvas(width, height)
我也希望能够像这样直接在 FXML 文件中添加 BrikkCanvas
<BrikkCanvas fx:id="myCanvas" width="100.0" height="100.0" rows="1" cols="1" />
我的 class 没有默认构造函数,这就是为什么将它包含在 FXML 中并非易事。
不过,我发现您可以实现自定义 BuilderFactory
,所以我做到了:
class BrikkCanvasBuilderFactory : BuilderFactory {
private val defaultBuilderFactory = JavaFXBuilderFactory()
override fun getBuilder(clazz: Class<*>): Builder<*> =
if (clazz == BrikkCanvas::class.java) BrikkCanvasBuilder()
else defaultBuilderFactory.getBuilder(clazz)
private class BrikkCanvasBuilder : Builder<BrikkCanvas> {
var width: Double = 0.0
var height: Double = 0.0
var rows: Int = 0
var cols: Int = 0
override fun build(): BrikkCanvas = BrikkCanvas(width, height, rows, cols)
}
}
在扩展 Application
的 App
class 中,我这样使用 BrikkCanvasBuilderFactory
:
fun startTheGame(playerName: String) {
val loader = FXMLLoader(App::class.java.getResource("game.fxml"), null, BrikkCanvasBuilderFactory())
val scene = Scene(loader.load())
val controller = loader.getController<GameController>()
primaryStage.scene = scene
}
但是,当我启动应用程序并单击调用 startTheGame
的按钮时,出现以下错误:
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
...
Caused by: java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
...
Caused by: javafx.fxml.LoadException:
/path to project/target/classes/game.fxml:21
at App$Companion.startTheGame(App.kt:18)
...
Caused by: java.lang.IllegalStateException: defaultBuilderFactory.getBuilder(clazz) must not be null
at controller.BrikkCanvasBuilderFactory.getBuilder(BrikkCanvasBuilderFactory.kt:12)
game.fxml:21
= fx:controller="controller.GameController"
App$Companion.startTheGame(App.kt:18)
= val scene = Scene(loader.load())
BrikkCanvasBuilderFactory.kt:12
= else defaultBuilderFactory.getBuilder(clazz)
请注意,我什至还没有在 FXML 文件中包含任何 <BrikkCanvas ... />
标签
我很确定我遗漏了一些非常基本的东西,这可能与我使用 Kotlin 而不是 Java
这一事实有关据我了解,BuilderFactory
和 Builder
接口是过去的遗物(基本上已弃用),应在新代码中避免使用——假装它们不存在。您应该改为使用 @NamedArg
注释来注释您的构造函数参数。
class BrikkCanvas(
@NamedArg("width") width: Double,
@NamedArg("height") height: Double,
@param:NamedArg("rows") private val rows: Int,
@param:NamedArg("cols") private val cols: Int
) : Canvas(width, height)
然后 FXMLLoader
将能够分辨出哪个 FXML 属性对应于哪个参数,从而可以通过构造函数设置属性。