4

我正在尝试遵循此 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,String] 需要:javafx.util.Callback[javafx.scene.control.TableColumn.CellDataFeatures[com.myapp.models.Foo,?0 ],javafx.beans.value.ObservableValue[?0]] where type ?0 c.setCellValueFactory(new PropertyValueFactoryFoo, String)

如果我删除该行,我可以确认一切正常setCellValueFactory——我只是在表格中看不到任何内容——只是空白行。

4

2 回答 2

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 ())。所以你的cval 输入为TableColumn[Foo,_]. 这意味着您丢失了单元格内容的类型(由第二个类型参数表示)。

这里唯一的解决方案是使用强制类型转换正确键入列 ( c):

val c = test.getColumns.get(0).asInstanceOf[TableColumn[Foo, String]]
c.setCellValueFactory(new PropertyValueFactory[Foo, String]("message"))

这是非常合乎逻辑的:您有一个列列表,其中每列可以包含不同类型的对象。对于包含不同类型对象的 scala List,我们将面临同样的困境,所有先验都是未知的:只有强制转换(或类型匹配,这是变相的强制转换)才能让您在编译时返回特定类型时间。

另请注意,在网络上的许多 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为泛型类型(但使用未知单元类型)会发生的情况进行对比:

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;
...

这就像在 scalac中输入为 TableColumn[Foo, _]

于 2012-10-10T14:50:27.900 回答
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);
    }
}
于 2012-10-10T08:09:09.817 回答