从列表 属性 到任意对象 属性 的 javafx 绑定
javafx binding from list property to arbitrary object property
我正在尝试让 class 将 属性 绑定到另一个 class 的列表 属性,其中第一个 属性从对列表中的对象进行汇总计算得出。下面的代码是我的生产代码的简化版本。 (生产代码正在对 DateTime 对象做总结——下面代码的本质部分是列表和对象之间的绑定 属性(这里为简单起见,它是一个 String)。)
我尝试过各种方法。一种方法是在下面的摘要 class 中的列表中使用 addListener
,但我 运行 陷入奇怪的错误,监听器回调对摘要对象进行更新。在阅读了大量内容后,我认为摘要字符串和列表之间的绑定更合适,但我不知道如何将绑定连接到 属性?
package com.example.demo.view
import javafx.beans.Observable
import javafx.beans.binding.StringBinding
import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleListProperty
import javafx.beans.property.SimpleStringProperty
import javafx.collections.FXCollections
import tornadofx.View
import tornadofx.button
import tornadofx.label
import tornadofx.vbox
class Thing(x: Int) {
val xProperty = SimpleIntegerProperty(x)
val yProperty = SimpleStringProperty("xyz")
}
class Collection {
private var things = FXCollections.observableList(mutableListOf<Thing>()) {
arrayOf<Observable>(it.xProperty)
}
val thingsProperty = SimpleListProperty<Thing>(things)
fun addThing(thing: Thing) {
things.add(thing)
}
}
class Summary(var collection: Collection) {
val summaryBinding = object : StringBinding() {
// The real code is more practical but
// this is just a minimal example.
override fun computeValue(): String {
val sum = collection.thingsProperty.value
.map { it.xProperty.value }
.fold(0, { total, next -> total + next })
return "There are $sum things."
}
}
// How to make this property update when collection changes?
val summaryProperty = SimpleStringProperty("There are ? things.")
}
class MainView : View() {
val summary = Summary(Collection())
override val root = vbox {
label(summary.summaryProperty)
button("Add Thing") {
summary.collection.addThing(Thing(5))
}
}
}
请记住,我是根据您的最小示例做出此回答的:
class Thing(x: Int) {
val xProperty = SimpleIntegerProperty(x)
var x by xProperty
val yProperty = SimpleStringProperty("xyz")
var y by yProperty
}
class MainView : View() {
val things = FXCollections.observableList(mutableListOf<Thing>()) {
arrayOf<Observable>(it.xProperty)
}
val thingsProperty = SimpleListProperty<Thing>(things)
val totalBinding = integerBinding(listProperty) {
value.map { it.x }.fold(0, { total, next -> total + next })
}
val phraseBinding = stringBinding(totalBinding) { "There are $value things." }
override val root = vbox {
label(phraseBinding)
button("Add Thing") {
action {
list.add(Thing(5))
}
}
}
}
我删除了您的其他 class,因为根据示例我没有看到删除它们的原因。如果集合 class 比在您的实际项目中保存列表 属性 具有更多功能,那么只需将其添加回去即可。如果没有,则没有理由为列表提供自己的 class.摘要 class 实际上只是两个绑定(如果您不需要将总数与短语分开,则为一个)。我不认为有必要给他们自己的 class 除非你计划在多个视图中使用它们。
我认为您最大的问题是您没有将按钮的操作包装在 action {}
中。所以你的代码只是在 init 上添加了一个 Thing(5)
并且没有设置任何操作。
P.S。 var x by xProperty
内容只有在您 import tornadofx.*
该文件时才有效。
我正在尝试让 class 将 属性 绑定到另一个 class 的列表 属性,其中第一个 属性从对列表中的对象进行汇总计算得出。下面的代码是我的生产代码的简化版本。 (生产代码正在对 DateTime 对象做总结——下面代码的本质部分是列表和对象之间的绑定 属性(这里为简单起见,它是一个 String)。)
我尝试过各种方法。一种方法是在下面的摘要 class 中的列表中使用 addListener
,但我 运行 陷入奇怪的错误,监听器回调对摘要对象进行更新。在阅读了大量内容后,我认为摘要字符串和列表之间的绑定更合适,但我不知道如何将绑定连接到 属性?
package com.example.demo.view
import javafx.beans.Observable
import javafx.beans.binding.StringBinding
import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleListProperty
import javafx.beans.property.SimpleStringProperty
import javafx.collections.FXCollections
import tornadofx.View
import tornadofx.button
import tornadofx.label
import tornadofx.vbox
class Thing(x: Int) {
val xProperty = SimpleIntegerProperty(x)
val yProperty = SimpleStringProperty("xyz")
}
class Collection {
private var things = FXCollections.observableList(mutableListOf<Thing>()) {
arrayOf<Observable>(it.xProperty)
}
val thingsProperty = SimpleListProperty<Thing>(things)
fun addThing(thing: Thing) {
things.add(thing)
}
}
class Summary(var collection: Collection) {
val summaryBinding = object : StringBinding() {
// The real code is more practical but
// this is just a minimal example.
override fun computeValue(): String {
val sum = collection.thingsProperty.value
.map { it.xProperty.value }
.fold(0, { total, next -> total + next })
return "There are $sum things."
}
}
// How to make this property update when collection changes?
val summaryProperty = SimpleStringProperty("There are ? things.")
}
class MainView : View() {
val summary = Summary(Collection())
override val root = vbox {
label(summary.summaryProperty)
button("Add Thing") {
summary.collection.addThing(Thing(5))
}
}
}
请记住,我是根据您的最小示例做出此回答的:
class Thing(x: Int) {
val xProperty = SimpleIntegerProperty(x)
var x by xProperty
val yProperty = SimpleStringProperty("xyz")
var y by yProperty
}
class MainView : View() {
val things = FXCollections.observableList(mutableListOf<Thing>()) {
arrayOf<Observable>(it.xProperty)
}
val thingsProperty = SimpleListProperty<Thing>(things)
val totalBinding = integerBinding(listProperty) {
value.map { it.x }.fold(0, { total, next -> total + next })
}
val phraseBinding = stringBinding(totalBinding) { "There are $value things." }
override val root = vbox {
label(phraseBinding)
button("Add Thing") {
action {
list.add(Thing(5))
}
}
}
}
我删除了您的其他 class,因为根据示例我没有看到删除它们的原因。如果集合 class 比在您的实际项目中保存列表 属性 具有更多功能,那么只需将其添加回去即可。如果没有,则没有理由为列表提供自己的 class.摘要 class 实际上只是两个绑定(如果您不需要将总数与短语分开,则为一个)。我不认为有必要给他们自己的 class 除非你计划在多个视图中使用它们。
我认为您最大的问题是您没有将按钮的操作包装在 action {}
中。所以你的代码只是在 init 上添加了一个 Thing(5)
并且没有设置任何操作。
P.S。 var x by xProperty
内容只有在您 import tornadofx.*
该文件时才有效。