TreeTableView的JavaFX自定义单元格渲染器

时间:2018-03-29 18:20:23

标签: javafx treetableview

我正在尝试在JavaFX中实现一个TreeTableView,其中包含' MyData'对象,并有两列。第一列应包含一个字符串;这很简单:

column1.setCellValueFactory((TreeTableColumn.CellDataFeatures<MyData, String> entry) 
-> new ReadOnlyStringWrapper(entry.getValue().getValue().toString()));

对于第二列,我需要在MyData对象中使用一些更复杂的数据,我想基本上渲染一系列描绘该数据的图标。所以,我尝试创建一个自定义单元格渲染器:

MyCellRenderer extends TreeTableCell<MyData, MyData> {
    @Override
    protected void updateItem(MyData item, boolean empty) {
        super.updateItem(item, empty);
        if (item == null || empty) {
            setGraphic(null);
            setText(null);
        } else {
            // building some ContentPane with an HBox of Images here..
            setGraphic(contentPane);
        }
    }
}

然后按如下所示设置CellFactory和CellValueFactory列:

column2.setCellValueFactory((TreeTableColumn.CellDataFeatures<MyData, MyData> entry) 
-> new ReadOnlyObjectWrapper(entry));

column2.setCellFactory(param -> new MyCellRenderer());

但是我在运行时得到了这个异常:

  

线程中的异常&#34; JavaFX应用程序线程&#34;   java.lang.ClassCastException:   javafx.scene.control.TreeTableColumn $ CellDataFeatures无法强制转换   到MyData

我担心我并不真正理解所有这些类的不同泛型类型的含义,而且我也不确定&#34; ReadOnlyObjectWrapper&#34;。我只是尝试从第一列的设置中复制/粘贴和调整它。

如果有人能对我有所启发,我将非常感激。不幸的是,关于TreeTableView的oracle文档没有详细介绍,只是展示了简单的例子。

谢谢

2 个答案:

答案 0 :(得分:1)

你需要

column2.setCellValueFactory((TreeTableColumn.CellDataFeatures<MyData, MyData> entry) 
    -> new ReadOnlyObjectWrapper<>(entry.getValue().getValue()));

请注意,如果您不使用原始类型(即使用ReadOnlyObjectWrapper<>ReadOnlyObjectWrapper<MyData>而不只是ReadOnlyObjectWrapper),编译器会通知您错误,这很多比试图破解运行时出错的东西更好。

如您所见,单元格值工厂的参数类型为TreeTableColumn.CellDataFeatures(请参阅docs)。这只是行值的包装器,您将从中提取单元格中显示的数据;这个包装器只包含行本身的树项(使用getValue()获得),以及单元格值工厂所附加的列(getTreeTableColumn())以及该列所属的表属于(getTreeTableView())。

我认为,后两者旨在使您能够编写一般的,可重复使用的单元值工厂,您可能希望根据它们所附加的列或表来自定义它们。 (用例对我来说很难设想,但我怀疑他们有一些机会...)

包含行的TreeItem(您使用entry.getValue()获得的行)当然包含行值(您可以使用getValue()获取此行,这就是为什么您最终会遇到{ {1}}),以及其他entry.getValue().getValue()特定信息(扩展,选择等等)。

答案 1 :(得分:1)

您将entry类型TreeTableColumn.CellDataFeatures<MyData, MyData>作为初始值传递给新的ReadOnlyObjectWraper - 原始类型 - 期望类型为{{1在运行时而不是MyData。如您所见,泛型类型不匹配。

尝试更改

TreeTableColumn.CellDataFeatures<MyData, MyData>

new ReadOnlyObjectWrapper(entry) 

两次new ReadOnlyObjectWrapper<>(entry.getValue().getValue()) 来电的原因是因为第一个getValue()返回entry.getValue()而第二个TreeItem<MyData>返回实际的getValue()个实例。

这是假设您的表已声明为MyData并且您的列已声明为TreeTableView<MyData>

编辑:由于您说您不太了解所有通用签名,因此这里有一个简短的解释。

TreeTableColumn<MyData, MyData>

此处TreeTableView<S> S显示的对象类型。 AKA,模型类。一个示例是TreeTableView类,它会使Person成为S

Person

此处的TreeTableColumn<S, T> SS的{​​{1}}相同,该列必定是该列的一部分。 TreeTableView是列中T将显示的对象类型。这通常是类型TreeTableCell的属性中包含的值。例如S的{​​{1}}名称会使StringProperty成为Person

T

StringTreeTableCell<S, T> 将与该单元格所属的S相同。

现在,对于值回调:

T

同样,TreeTableColumnCallback<TreeTableColumn.CellDataFeatures<S, T>, ObservableValue<T>> 代表S所属的T相同类型。此TreeTableColumn返回包含Callback类型的Callback,以便ObservableValue可以观察更改的值并相应地更新UI。在您的情况下,由于您要显示的类型不在属性中,因此您返回新的T以满足API要求。如果我继续使用上面提到的名称TreeTableCell,您最终会得到类似的结果:

ReadOnlyObjectWrapper