为什么在 TornadoFx 中在 root 之后声明的变量会自动添加到 UI?
Why are variables declared after root automatically added to UI in TornadoFx?
我偶然发现了 TornadoFx 中的一个行为,它似乎在任何地方都没有被提及(我已经搜索了很多),我对此感到疑惑。
如果我使用 TornadoFx 构建器为标签定义这样的视图:
class ExampleView: View() {
override
val root = vbox{ label("first label") }
val secondLabel = label("second label")
}
结果是:
即单纯定义secondLabel
自动添加到场景root
中。
但是,如果我将此定义放在 root
...
的定义之前
class ExampleView: View() {
val secondLabel = Label("second label")
override
val root = vbox{ label("first label") }
}
... 或者如果我使用 JavaFx Label
class 而不是 TornadoFx 构建器...
class ExampleView: View() {
override
val root = vbox{ label("first label") }
val secondLabel = Label("second label")
}
...然后它按我预期的那样工作:
当然,我可以在定义 root
元素之前简单地定义视图中的所有变量,但我仍然很好奇为什么会存在这种行为;也许我遗漏了一些通用的设计规则或设置。
TornadoFX 中的 builder 会自动将自身附加到调用它们的范围内的当前父级。因此,如果您在视图本身上调用 builder 函数,生成的ui 组件自动添加到该视图的根。这就是您所看到的。
如果你真的有一个有效的用例来创建一个 ui 组件在层次结构之外,它应该被安置在里面,你不应该调用 builder 函数,而是实例化元素及其构造函数,就像您对 Label()
所做的那样。但是,这种行为的用例很少 none。
最佳做法是在视图或视图模型中存储值属性,并使用 bui 连接器将 属性 绑定到 ui 元素。然后在需要时操作值 属性,更改将自动更新到 ui。因此,您很少需要在稍后阶段访问特定的 ui 元素。示例:
val myProperty = SimpleStringProperty("Hello world")
override val root = hbox {
label(myProperty)
}
当您想更改标签值时,只需更新 属性。 (属性 应该在真实世界应用程序中的注入视图模型中)。
如果你真的需要引用ui元素,你应该先声明ui 属性,然后在实际b[=29=时赋值给它]ld ui 元素。使用 singleAssign()
委托定义 ui 属性 以确保只分配给它一次。
var myLabel: Label by singleAssign()
override val root = hbox {
label("My label) {
myLabel = this
}
}
我想再次强调,这很少需要,如果你觉得你需要它,你应该考虑重组你的 ui 代码以更多地由数据驱动。
另一种避免存储对 ui 元素的引用的技术是利用 EventBus 来侦听事件。有很多这样的例子。
我偶然发现了 TornadoFx 中的一个行为,它似乎在任何地方都没有被提及(我已经搜索了很多),我对此感到疑惑。
如果我使用 TornadoFx 构建器为标签定义这样的视图:
class ExampleView: View() {
override
val root = vbox{ label("first label") }
val secondLabel = label("second label")
}
结果是:
即单纯定义secondLabel
自动添加到场景root
中。
但是,如果我将此定义放在 root
...
class ExampleView: View() {
val secondLabel = Label("second label")
override
val root = vbox{ label("first label") }
}
... 或者如果我使用 JavaFx Label
class 而不是 TornadoFx 构建器...
class ExampleView: View() {
override
val root = vbox{ label("first label") }
val secondLabel = Label("second label")
}
...然后它按我预期的那样工作:
当然,我可以在定义 root
元素之前简单地定义视图中的所有变量,但我仍然很好奇为什么会存在这种行为;也许我遗漏了一些通用的设计规则或设置。
TornadoFX 中的 builder 会自动将自身附加到调用它们的范围内的当前父级。因此,如果您在视图本身上调用 builder 函数,生成的ui 组件自动添加到该视图的根。这就是您所看到的。
如果你真的有一个有效的用例来创建一个 ui 组件在层次结构之外,它应该被安置在里面,你不应该调用 builder 函数,而是实例化元素及其构造函数,就像您对 Label()
所做的那样。但是,这种行为的用例很少 none。
最佳做法是在视图或视图模型中存储值属性,并使用 bui 连接器将 属性 绑定到 ui 元素。然后在需要时操作值 属性,更改将自动更新到 ui。因此,您很少需要在稍后阶段访问特定的 ui 元素。示例:
val myProperty = SimpleStringProperty("Hello world")
override val root = hbox {
label(myProperty)
}
当您想更改标签值时,只需更新 属性。 (属性 应该在真实世界应用程序中的注入视图模型中)。
如果你真的需要引用ui元素,你应该先声明ui 属性,然后在实际b[=29=时赋值给它]ld ui 元素。使用 singleAssign()
委托定义 ui 属性 以确保只分配给它一次。
var myLabel: Label by singleAssign()
override val root = hbox {
label("My label) {
myLabel = this
}
}
我想再次强调,这很少需要,如果你觉得你需要它,你应该考虑重组你的 ui 代码以更多地由数据驱动。
另一种避免存储对 ui 元素的引用的技术是利用 EventBus 来侦听事件。有很多这样的例子。