我从几周开始就开始使用JavaFX,在花了很长时间处理
TableView
和TreeTableView
之后,我来找你是为了更好地了解cellFactory
。
我的第一个问题涉及 JavaFX 中
cellFactory
的全局方法。
通常工厂被设计为生产具有不同行为的不同对象。在 JavaFX 中,
cellFactory
似乎为整个列创建相同类型的 Cell
。
这种方法尊重工厂设计模式吗?
如果我想根据
Cell
的内容(值)更改 Cell
的行为,我不能(正如我到目前为止所看到的)。
所以我的第一个问题是:
如何根据单元格的内容为
cellFactory
中的每个Cell
创建自定义TableColumn
?
这种方法尊重工厂设计模式吗?
是的,确实如此。工厂模式的全部要点是允许使用它的类 (
TableView
) 创建遵守特定契约 (TableCell
) 的类实例,但隐藏具体实现。
本页图表所示的工作流程:http://www.oodesign.com/factory-pattern.html
只需将
Client
替换为 TableView
,将 Product
替换为 TableCell
...
这允许为
cellFactory
和 cellValueFactory
设计可重用的类,而无需更改任何扩展 TableView
/ TableColumn
的内容。 (只需看一下 javafx.scene.control.cell
包;其中的大多数(甚至所有)单元格都有一个静态方法来为其类型创建工厂;这些工作独立于 TableView
项目,并且可以一起使用,例如与PropertyValueFactory
)。
如何根据单元格的内容为 TableColumn 中的每个单元格创建自定义单元格工厂?
你不知道。您编写一个表格单元格,根据通过修改
text
和 graphic
属性等传递的内容来决定其布局。当项目更改时(item
属性侦听器或updateItem
方法)。
通过显示使用默认构造函数创建的实例来显示类的单元格示例:
column1.setCellFactory(v -> new TableCell<Class<? extends Node>, Class<? extends Node>>() {
@Override
protected void updateItem(Class<? extends Node> item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
try {
setGraphic(item.getConstructor().newInstance());
} catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | InvocationTargetException ex) {
setGraphic(null);
}
}
}
});
这假设您可以根据给定的项目值决定布局。
如果您无法使用这种方法设计干净的代码,请考虑将其与策略模式结合起来,即让您的项目决定
TableCell
的布局:
@FunctionalInterface
public interface TableCellLayouter {
void layoutCell(TableCell<?, ?> cell);
}
// the content could be more complex of course
TableView<TableCellLayouter> tv = new TableView<>(FXCollections.observableArrayList(
cell -> {
cell.setText(null);
cell.setGraphic(new Rectangle(100, 100));
},
cell -> {
cell.setGraphic(null);
cell.setText("Hello world!");
},
cell -> {
cell.setGraphic(new Circle(20));
cell.setText("circle");
}));
TableColumn<TableCellLayouter, TableCellLayouter> column1 = new TableColumn<>("a");
column1.setCellValueFactory(c -> Bindings.createObjectBinding(() -> c.getValue()));
column1.setCellFactory(v -> new TableCell<TableCellLayouter, TableCellLayouter>() {
@Override
protected void updateItem(TableCellLayouter item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
setText(null);
} else {
item.layoutCell(this);
}
}
});