用于 TreeTableView 的 JavaFX 自定义单元格渲染器
JavaFX Custom Cell Renderer for 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());
但是我在运行时得到这个异常:
Exception in thread "JavaFX Application Thread"
java.lang.ClassCastException:
javafx.scene.control.TreeTableColumn$CellDataFeatures cannot be cast
to MyData
恐怕我并不真正理解所有这些 类 的不同泛型类型的含义,而且我也不确定 "ReadOnlyObjectWrapper"。我只是尝试 copy/paste 并从第一列的设置中对其进行调整。
如果有人能对我有所启发,我将不胜感激。不幸的是,关于 TreeTableView 的 oracle 文档并没有涉及那么多细节,它们只是展示了简单的示例。
谢谢
你需要
column2.setCellValueFactory((TreeTableColumn.CellDataFeatures<MyData, MyData> entry)
-> new ReadOnlyObjectWrapper<>(entry.getValue().getValue()));
请注意,如果您不使用原始类型(即使用 ReadOnlyObjectWrapper<>
或 ReadOnlyObjectWrapper<MyData>
,而不仅仅是 ReadOnlyObjectWrapper
),编译器会通知您错误,这比试图破译运行时出了什么问题要好得多。
如您所见,单元格值工厂的参数类型是 TreeTableColumn.CellDataFeatures
(参见 docs)。这只是行值的包装器,您将从中提取单元格中显示的数据;这个包装器只包含行本身的树项(通过 getValue()
获得),以及单元格值工厂附加到的列 (getTreeTableColumn()
) 和 table该列所属的 (getTreeTableView()
)。
我认为,后两者旨在让您能够编写通用的、可重用的单元格值工厂,您可能希望根据列或 table 对其进行自定义随附的。 (这方面的用例对我来说很难想象,但我怀疑他们有一些机会......)
包含行的 TreeItem
(用 entry.getValue()
得到)当然包含行值本身(用 getValue()
得到,这就是为什么你最终entry.getValue().getValue()
),以及其他 TreeItem
特定信息(是否展开、选择等)。
您正在将类型为 TreeTableColumn.CellDataFeatures<MyData, MyData>
的 entry
作为初始值传递给新的 ReadOnlyObjectWraper
- 原始类型 - 需要 MyData
在运行时而不是 TreeTableColumn.CellDataFeatures<MyData, MyData>
。如您所见,泛型类型不匹配。
尝试更改
new ReadOnlyObjectWrapper(entry)
到
new ReadOnlyObjectWrapper<>(entry.getValue().getValue())
两次 getValue()
调用的原因是第一个 entry.getValue()
return 是 TreeItem<MyData>
而第二个 getValue()
return 是实际 MyData
个实例。
这一切都假设您的 table 声明为 TreeTableView<MyData>
并且您的列声明为 TreeTableColumn<MyData, MyData>
。
编辑:既然你说你并不真正理解所有的通用签名,这里有一个简短的解释。
TreeTableView<S>
这里的S
是TreeTableView
显示的对象类型。又名,模型 class。一个例子是 Person
class 这将使 S
成为 Person
.
TreeTableColumn<S, T>
这里的S
和TreeTableView
中的S
是一样的,该列注定是其中的一部分。 T
是列中 TreeTableCell
将显示的对象类型。这通常是 S
类型的 属性 中包含的值。例如 StringProperty
的名称 Person
会使 T
成为 String
.
TreeTableCell<S, T>
S
和 T
与单元格所属的 TreeTableColumn
相同。
现在,对于值回调:
Callback<TreeTableColumn.CellDataFeatures<S, T>, ObservableValue<T>>
同样,S
和 T
表示 Callback
所属的 TreeTableColumn
的相同类型。这个 Callback
return 是一个包含类型 T
的 ObservableValue
以便 TreeTableCell
可以观察变化的值并相应地更新 UI .在您的情况下,由于您要显示的类型不在 属性 中,您 return 一个新的 ReadOnlyObjectWrapper
来满足 API 要求。如果我继续使用上面给出的名称 StringProperty
示例,您可能会得到如下结果:
TreeTableView<Person> table = ..;
TreeTableColumn<Person, String> column = ...;
column.setCellValueFactory(dataFeatures -> {
// This could all be done in one line but I figured I'd
// make it explicit to show all the types used.
TreeItem<Person> item = dataFeatures.getValue();
Person person = item.getValue();
return person.nameProperty(); // returns StringProperty which is an
// ObservableStringValue which in turn
// is an ObservableValue<String>
});
我正在尝试在 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());
但是我在运行时得到这个异常:
Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: javafx.scene.control.TreeTableColumn$CellDataFeatures cannot be cast to MyData
恐怕我并不真正理解所有这些 类 的不同泛型类型的含义,而且我也不确定 "ReadOnlyObjectWrapper"。我只是尝试 copy/paste 并从第一列的设置中对其进行调整。
如果有人能对我有所启发,我将不胜感激。不幸的是,关于 TreeTableView 的 oracle 文档并没有涉及那么多细节,它们只是展示了简单的示例。
谢谢
你需要
column2.setCellValueFactory((TreeTableColumn.CellDataFeatures<MyData, MyData> entry)
-> new ReadOnlyObjectWrapper<>(entry.getValue().getValue()));
请注意,如果您不使用原始类型(即使用 ReadOnlyObjectWrapper<>
或 ReadOnlyObjectWrapper<MyData>
,而不仅仅是 ReadOnlyObjectWrapper
),编译器会通知您错误,这比试图破译运行时出了什么问题要好得多。
如您所见,单元格值工厂的参数类型是 TreeTableColumn.CellDataFeatures
(参见 docs)。这只是行值的包装器,您将从中提取单元格中显示的数据;这个包装器只包含行本身的树项(通过 getValue()
获得),以及单元格值工厂附加到的列 (getTreeTableColumn()
) 和 table该列所属的 (getTreeTableView()
)。
我认为,后两者旨在让您能够编写通用的、可重用的单元格值工厂,您可能希望根据列或 table 对其进行自定义随附的。 (这方面的用例对我来说很难想象,但我怀疑他们有一些机会......)
包含行的 TreeItem
(用 entry.getValue()
得到)当然包含行值本身(用 getValue()
得到,这就是为什么你最终entry.getValue().getValue()
),以及其他 TreeItem
特定信息(是否展开、选择等)。
您正在将类型为 TreeTableColumn.CellDataFeatures<MyData, MyData>
的 entry
作为初始值传递给新的 ReadOnlyObjectWraper
- 原始类型 - 需要 MyData
在运行时而不是 TreeTableColumn.CellDataFeatures<MyData, MyData>
。如您所见,泛型类型不匹配。
尝试更改
new ReadOnlyObjectWrapper(entry)
到
new ReadOnlyObjectWrapper<>(entry.getValue().getValue())
两次 getValue()
调用的原因是第一个 entry.getValue()
return 是 TreeItem<MyData>
而第二个 getValue()
return 是实际 MyData
个实例。
这一切都假设您的 table 声明为 TreeTableView<MyData>
并且您的列声明为 TreeTableColumn<MyData, MyData>
。
编辑:既然你说你并不真正理解所有的通用签名,这里有一个简短的解释。
TreeTableView<S>
这里的S
是TreeTableView
显示的对象类型。又名,模型 class。一个例子是 Person
class 这将使 S
成为 Person
.
TreeTableColumn<S, T>
这里的S
和TreeTableView
中的S
是一样的,该列注定是其中的一部分。 T
是列中 TreeTableCell
将显示的对象类型。这通常是 S
类型的 属性 中包含的值。例如 StringProperty
的名称 Person
会使 T
成为 String
.
TreeTableCell<S, T>
S
和 T
与单元格所属的 TreeTableColumn
相同。
现在,对于值回调:
Callback<TreeTableColumn.CellDataFeatures<S, T>, ObservableValue<T>>
同样,S
和 T
表示 Callback
所属的 TreeTableColumn
的相同类型。这个 Callback
return 是一个包含类型 T
的 ObservableValue
以便 TreeTableCell
可以观察变化的值并相应地更新 UI .在您的情况下,由于您要显示的类型不在 属性 中,您 return 一个新的 ReadOnlyObjectWrapper
来满足 API 要求。如果我继续使用上面给出的名称 StringProperty
示例,您可能会得到如下结果:
TreeTableView<Person> table = ..;
TreeTableColumn<Person, String> column = ...;
column.setCellValueFactory(dataFeatures -> {
// This could all be done in one line but I figured I'd
// make it explicit to show all the types used.
TreeItem<Person> item = dataFeatures.getValue();
Person person = item.getValue();
return person.nameProperty(); // returns StringProperty which is an
// ObservableStringValue which in turn
// is an ObservableValue<String>
});