我正在尝试遵循此JavaFX 2指南:
http://docs.oracle.com/javafx/2/ui_controls/table-view.htm#CJAGAAEE
我使用Scala而不是Java,对我来说它看起来像这样:
<TableView fx:id="test">
<columns>
<TableColumn prefWidth="75.0" text="Message" />
</columns>
</TableView>
代码:
val c = test.getColumns.get(0) // Get the first column.
c.setCellValueFactory(new PropertyValueFactory[Foo, _]("message")) // Foo is my model with a single SimpleStringProperty called "message".
val observableFoos = FXCollections.observableList(foos)
test.setItems(observableFoos)
我遇到的问题是setCellValueFactory
行导致:
错误:需要类类型但是 javafx.scene.control.cell.PropertyValueFactory [com.myapp.models.Foo, _]找到c.setCellValueFactory(new PropertyValueFactoryFoo,_)
我不明白我应该如何使用这种方法。如果我将_
替换为String
,那么我会得到:
错误:类型不匹配;发现: javafx.scene.control.cell.PropertyValueFactory [com.myapp.models.Foo,字符串] 需要: javafx.util.Callback [javafx.scene.control.TableColumn.CellDataFeatures [com.myapp.models.Foo,?0],javafx.beans.value.ObservableValue [?0]] where?0 c.setCellValueFactory(new PropertyValueFactoryFoo, 字符串)
如果我删除setCellValueFactory
行,我可以确认一切正常 - 我只是没有看到表格中的任何内容 - 只是按预期显示空行..
答案 0 :(得分:5)
TL; DR:在java中,很容易在类型参数方面默默地绕过类型安全。
除了执行显式演员之外,Scala不会让你这样做。
此外,使用Table.getColumns
检索列意味着我们失去了单元格类型。
解决方案:将您的TableColumn
实例转换为正确的类型,或手动实例化该列。
第一个问题在于Table.getColumns
的签名:它返回ObservableList[TableColumn[S,_]]
(请参阅http://docs.oracle.com/javafx/2/api/javafx/scene/control/TableView.html#getColumns())。
因此,c
val的输入为TableColumn[Foo,_]
。
这意味着您将丢失单元格内容的类型(由第二个类型参数表示)。
这里唯一的解决方案是使用强制转换:
正确键入列(c
)
val c = test.getColumns.get(0).asInstanceOf[TableColumn[Foo, String]]
c.setCellValueFactory(new PropertyValueFactory[Foo, String]("message"))
这是非常合乎逻辑的:您有一个列列表,其中每列可以包含不同类型的对象。我们将面临同样的困境,scala List包含不同类型的对象,所有这些都是先验未知的:只有一个强制转换(或类型上的匹配,这是一个伪装的强制转换)将让你在编译时返回一个特定的类型时间。
另请注意,在Web上的许多JavaFx示例中,人们不会通过Table.getColumns
检索列。相反,他们手动实例化它们,然后立即调用setCellValueFactory
。
这样做可以解决您的问题(无需任何演员),因为您将自己指定类型参数:
val c = new TableColumn[Foo, String] // See ma, no cast!
c.setCellValueFactory(new PropertyValueFactory[Foo, String]("message"))
现在,您可以查看一些Java示例,并注意到在实例化TableColumn
时它们实际上并未提供任何类型参数:
TableColumn c = new TableColumn();
c.setCellValueFactory( new PropertyValueFactory<Foo,String>("name"));
确实它确实编译了。怎么会这样?令人遗憾的事实是,上面的代码并不比执行转换更安全,因为它依赖于java对预先通用的感知类的向后兼容性。
换句话说,因为c
仅通过示例键入为TableColumn
而不是TableColumn<?, String>
,因此java编译器将其视为非泛型类,而不执行任何关于类型参数的类型检查TableColumn
。
将此与您明确将c
的类型设置为泛型类型(但具有unknwon单元格类型)时所发生的情况进行对比:
TableColumn<Foo, ?> name = new TableColumn("Name");
name.setCellValueFactory( new PropertyValueFactory<Room,String>("name"));
在这种情况下,编译器会发出类型不匹配错误:
error: method setCellValueFactory in class TableColumn<S,T> cannot be applied to given types;
...
这就像在scala中输入c
作为TableColumn [Foo,_]
答案 1 :(得分:1)
我建议尝试使用PropertyValueFactory
的长格式:
(对不起,我不熟悉Scala所以用Java写的)
c.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Foo, String>,
ObservableValue<String>>() {
public ObservableValue<String> call(TableColumn.CellDataFeatures<Foo, String> f) {
// f.getValue() returns the Foo instance for a particular TableView row
return f.getValue().messageProperty();
// or
return new SimpleStringProperty(p.getValue().getMessage());
// if you omit messageProperty() in Foo model class.
// However omitting it, causes the property listener could not be attached resulting
// unable to refresh (synchronize) tableview row item value when the backing
// Foo's message value is changed.
}
});
请注意,您的Foo模型必须类似于:
public class Foo {
private SimpleStringProperty message;
public SimpleStringProperty messageProperty() {
return message;
}
public String getMessage() {
return message.get();
}
public void setMessage(String message) {
this.message.set(message);
}
}