我刚启动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]
而我预计细胞中只会出现“大”、“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显示属性对象的字符串表示,而不仅仅是显示值?
非常感谢!
现在我知道PropertyValueFactory使用反射来查找属性。我认为这是定义IntegerProperty或StringProperty的关键。因此,只需将模型类更改为以下类型即可解决问题:
class ErpSheet {
var name = ""
var sheet_long = 0
var sheet_short = 0
}
成员变量名是PropertyVaueFactory的关键。
当类公开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方法)返回属性对象,而不是属性值。
Kotlin对JavaFX属性的处理效果并不特别好。您需要创建两个Kotlin属性,一个用于JavaFX属性对象,另一个作为JavaFX属性值的委托(手动或通过关键字)。
这里有一个例子:
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)
}
例如,您可以看到,name
Kotlin属性将其getter和setter委托给
nameProperty
Kotlin属性。
@get: JvmName("nameProperty")
注解对于静态编程语言在Java端生成正确的"属性getter"方法(JVM字节码)是必要的。如果没有该注解,getter将被命名为getNameProperty()
,这与JavaFX属性的模式不匹配。如果您从未打算使用来自Java的静态编程语言代码,或者使用任何依赖反射的类(例如,Property tyValueFactory
)来获取属性,您可以不使用注解。
如果您想使用关键字而不是手动编写getter和setter(例如,
var-name:String-by-nameProperty),请参阅关于委托属性的Kotlin文档。您可以编写扩展函数来实现这一点。
下面是一个使用上述Person类的可运行示例。它还周期性地增加每个人的年龄,以便您可以看到表视图正在观察模型项。
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)
}
综上所述,无论您是用Java还是Kotlin编写应用程序,都应该避免使用PropertyValueFactory。它是在lambda表达式还不是Java的一部分时添加的,以帮助开发人员避免在任何地方编写冗长的匿名类。然而,它有两个缺点:它依赖于反射,更重要的是,您会丢失编译时验证(例如,属性是否确实存在)。
您应该用lambda替换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
问题内容: 我正在做一个从列表中返回最长字符串值的函数。当只有一个包含最多字符的字符串时,我的代码有效。如果有多个字符串,我尝试使其打印所有最长的字符串,并且我不希望重复它们。当我运行它时,它只返回“ hello”,而我希望它也返回“ ohman”和“ yoloo”。我觉得问题就在眼前,但是我已经尝试了所有方法,但是没有用。 问题答案: 首先 ,我们可以在列表中找到任何字符串的最大长度: 一点解释
如果我有一个< code>ArrayList的单词,比如: 我想计算 4 5 6 3 3 3 作为 中每个字符串的长度之和加上单词之间的 5 个空格。我该怎么做?
问题内容: 我想打印一个字符或字符串,例如’-‘n次。 我可以不使用循环就做吗? ..这意味着打印3次,如下所示: 问题答案: Python 2.x: Python 3.x:
我有代码在这里生成一个随机的名字给定音节点击一个动作按钮。但作为字符串currentName生成的名称会显示在TextView中。我需要使视图成为它自己的类吗?我是androidstudio的新手,来自eclipse,所以textviews对我来说也是新手。多谢了。 } 这也是我的activity_main.xml
问题内容: 最简单的方法是将类似以下的列表转换为: 即使用户在逗号之间加空格,也要在引号内加空格。我还需要处理以下内容: 在Python中。 我可以使用并使用split运算符删除空格,并检查非字母。但是代码变得非常混乱。有快点 方法吗? 问题答案: 使用,你可以安全地评估表达式节点或包含Python表达式的字符串。提供的字符串或节点只能由以下Python文字结构组成:字符串,数字,元组,列表,字典