JavaFx TableView显示列的长字符串表示



我刚启动JavaFx,我有点卡在TableView,它显示非常长的字符串表示每个列,如:

StringProperty [bean: com.plcsim2.PlcSimModel$ErpSheet@2c1ffe7b, name:name, value: big]
IntegerProperty [bean: com.plcsim2.PlcSimModel$ErpSheet@2c1ffe7b, name:sheet_long, value: 5000]
IntegerProperty [bean: com.plcsim2.PlcSimModel$ErpSheet@2c1ffe7b, name:sheet_short, value: 3000]

而我只期待"big", "5000", "3000"显示在单元格中。

这是我的模型:
object PlcSimModel {
class ErpSheet {
val name = SimpleStringProperty(this, "name")
val sheet_long = SimpleIntegerProperty(this, "sheet_long")
val sheet_short = SimpleIntegerProperty(this, "sheet_short")
}
val erpSheets = ArrayList<ErpSheet>()
}

fxml:

<VBox alignment="CENTER" prefHeight="562.0" prefWidth="812.0" spacing="20.0"
xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.plcsim2.PlcSimController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>
<TableView fx:id="table_1" prefHeight="400.0" prefWidth="200.0">
</TableView>
<Button onAction="#onHelloButtonClick" text="Hello!" />
</VBox>
最后是控制器:
@FXML
private fun onHelloButtonClick() {
val rs = DB.populateSql("select name, sheet_long, sheet_short from erp_sheet")
PlcSimModel.erpSheets.clear()
if (rs != null) {
while (rs.next()) {
val sheet = PlcSimModel.ErpSheet()
sheet.name.set(rs.getString("name"))
sheet.sheet_long.set(rs.getInt("sheet_long"))
sheet.sheet_short.set(rs.getInt("sheet_short"))
PlcSimModel.erpSheets.add(sheet)
}
}
table_1.columns.clear()
val col0 = TableColumn<PlcSimModel.ErpSheet, String>("name")
col0.cellValueFactory = PropertyValueFactory("name")
table_1.columns.add(col0)
val col1 = TableColumn<PlcSimModel.ErpSheet, Int>("sheet_long")
col1.cellValueFactory = PropertyValueFactory("sheet_long")
table_1.columns.add(col1)
val col2 = TableColumn<PlcSimModel.ErpSheet, Int>("sheet_short")
col2.cellValueFactory = PropertyValueFactory("sheet_short")
table_1.columns.add(col2)
table_1.items = FXCollections.observableArrayList(PlcSimModel.erpSheets)
}

似乎控制器是好的,它能够从数据库中获取值,并将行添加到TableView,但为什么TableView显示属性对象的字符串表示,而不是只是显示值?

非常感谢!

JavaFX Properties

当一个类公开JavaFX属性时,它应该遵循以下模式:

import javafx.beans.property.SimpleStringProperty
import javafx.beans.property.StringProperty
public class Foo {
// a field holding the property
private final StringProperty name = new SimpleStringProperty(this, "name");

// a setter method (but ONLY if the property is writable)
public final void setName(String name) {
this.name.set(name);
}
// a getter method
public final String getName() {
return name.get();
}
// a "property getter" method
public final StringProperty nameProperty() {
return name;
}
}

注意属性的名称是name,以及如何在getter、setter和属性getter方法的名称中使用它。方法名必须遵循该格式。

PropertyValueFactory类使用反射来获得所需的属性。它依赖于上面描述的方法命名模式。您的ErpSheet类不遵循上述模式。隐式getter方法(而不是属性getter方法)返回属性对象,而不是属性的


芬兰湾的科特林,JavaFX特性h1> otlin不能很好地处理JavaFX属性。您需要创建两个Kotlin属性,一个用于JavaFX属性对象,另一个作为JavaFX属性的的委托(手动或通过by

关键字)。下面是一个例子:

import javafx.beans.property.IntegerProperty
import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleStringProperty
import javafx.beans.property.StringProperty
class Person(name: String = "", age: Int = 0) {
@get:JvmName("nameProperty")
val nameProperty: StringProperty = SimpleStringProperty(this, "name", name)
var name: String
get() = nameProperty.get()
set(value) = nameProperty.set(value)
@get:JvmName("ageProperty")
val ageProperty: IntegerProperty = SimpleIntegerProperty(this, "age", age)
var age: Int
get() = ageProperty.get()
set(value) = ageProperty.set(value)
}

您可以看到,例如,nameKotlin属性将其getter和setter委托给namePropertyKotlin属性。

@get:JvmName("nameProperty")注释对于Kotlin生成正确的"属性getter"是必需的。方法(JVM字节码)。如果没有该注释,getter将被命名为getNameProperty(),这与JavaFX属性的模式不匹配。如果你从不打算使用Java中的Kotlin代码,或者使用任何依赖反射的类(例如,PropertyValueFactory)来获得属性,那么你可以不使用注释。

如果你想使用by关键字,而不是手动编写getter和setter(例如,var name: String by nameProperty),请参阅Kotlin文档中的委托属性。您可以为ObservableValue/WritableValue(以及ObservableIntegerValue/WritableIntegerValue等)编写扩展函数来实现此功能。

可运行示例

下面是一个使用上述Person类的可运行示例。它还定期增加每个Person的年龄,以便您可以看到TableView正在观察模型项。
import javafx.animation.PauseTransition
import javafx.application.Application
import javafx.beans.property.IntegerProperty
import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleStringProperty
import javafx.beans.property.StringProperty
import javafx.scene.Scene
import javafx.scene.control.TableColumn
import javafx.scene.control.TableView
import javafx.scene.control.cell.PropertyValueFactory
import javafx.stage.Stage
import javafx.util.Duration
fun main(args: Array<String>) = Application.launch(App::class.java, *args)
class App : Application() {
override fun start(primaryStage: Stage) {
val table = TableView<Person>()
table.columnResizePolicy = TableView.CONSTRAINED_RESIZE_POLICY
table.items.addAll(
Person("John Doe", 35),
Person("Jane Doe", 42)
)
val nameCol = TableColumn<Person, String>("Name")
nameCol.cellValueFactory = PropertyValueFactory("name")
table.columns += nameCol
val ageCol = TableColumn<Person, Number>("Age")
ageCol.cellValueFactory = PropertyValueFactory("age")
table.columns += ageCol
primaryStage.scene = Scene(table, 600.0, 400.0)
primaryStage.show()
PauseTransition(Duration.seconds(1.0)).apply {
setOnFinished {
println("Incrementing age of each person...")
table.items.forEach { person -> person.age += 1 }
playFromStart()
}
play()
}
}
}
class Person(name: String = "", age: Int = 0) {
@get:JvmName("nameProperty")
val nameProperty: StringProperty = SimpleStringProperty(this, "name", name)
var name: String
get() = nameProperty.get()
set(value) = nameProperty.set(value)
@get:JvmName("ageProperty")
val ageProperty: IntegerProperty = SimpleIntegerProperty(this, "age", age)
var age: Int
get() = ageProperty.get()
set(value) = ageProperty.set(value)
}

避免PropertyValueFactory

综上所述,无论您是用Java还是Kotlin编写应用程序,都应该避免使用PropertyValueFactory。它是在lambda表达式还不是Java的一部分时添加的,以帮助开发人员避免到处编写冗长的匿名类。然而,它有两个缺点:它依赖于反射,更重要的是,您会丢失编译时验证(例如,属性是否实际存在)。

您应该用lambdas替换PropertyValueFactory的使用。例如,从上面的代码中替换为:

val nameCol = TableColumn<Person, String>("Name")
nameCol.cellValueFactory = PropertyValueFactory("name")
table.columns += nameCol
val ageCol = TableColumn<Person, Number>("Age")
ageCol.cellValueFactory = PropertyValueFactory("age")
table.columns += ageCol

:

val nameCol = TableColumn<Person, String>("Name")
nameCol.setCellValueFactory { it.value.nameProperty }
table.columns += nameCol
val ageCol = TableColumn<Person, Number>("Age")
ageCol.setCellValueFactory { it.value.ageProperty }
table.columns += ageCol

现在我知道PropertyValueFactory使用反射来查找属性。我认为这是定义到IntegerProperty或StringProperty的关键。因此,只需将模型类更改为以下内容即可解决问题:

class ErpSheet {
var name = ""
var sheet_long = 0
var sheet_short = 0
}

成员变量名是PropertyVaueFactory的关键。

最新更新