{TableView}由许多TableColumn实例组成。 表格中的每个TableColumn负责显示(和编辑)该列的内容。 TableColumn不仅负责显示和编辑单个列的数据,还包含以下必要属性:
调整大小(使用{#minWidthProperty()minWidth} / {#prefWidthProperty()prefWidth} / {#maxWidthProperty()maxWidth}和{#widthProperty()width}属性)
切换其可见性{#visibleProperty()}
显示标头文本{#textProperty()}
显示它可能包含的任何嵌套列{#getColumns()}
当用户右键单击列标题区域时,具有上下文菜单{#contextMenuProperty()}
对表的内容进行排序(使用{#comparatorProperty()比较器},{#sortable sortable}和{#sortTypeProperty()sortType})
创建TableColumn实例时,可能要设置的两个最重要的属性是列文本{#textProperty()}(列标题区域中显示的内容)和列单元格值工厂{#cellValueFactory}( 用于填充列中的单个单元格)。
这可以通过对以下代码进行一些更改来实现:
ObservableList<Person> data = ...
TableView<Person> tableView = new TableView<Person>(data);
TableColumn<Person,String> firstNameCol = new TableColumn<Person,String>("First Name");
firstNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
public ObservableValue<String> call(CellDataFeatures<Person, String> p) {
// p.getValue() 返回特定TableView行的Person实例
return p.getValue().firstNameProperty();
}
});
}
tableView.getColumns().add(firstNameCol);}
此方法假定从 p.getValue()返回的对象具有可以简单返回的JavaFX {ObservableValue}。
这样做的好处是TableView将在内部创建绑定,以确保如果返回的{ObservableValue}发生更改,则单元格内容将自动刷新。
如果TableColumn必须与JavaFX之前创建的类进行交互,或者通常不希望将JavaFX api用于属性,则可以将返回的值包装在{@link ReadOnlyObjectWrapper}实例中。 例如:
firstNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
public ObservableValue<String> call(CellDataFeatures<Person, String> p) {
return new ReadOnlyObjectWrapper(p.getValue().getFirstName());
}
});
最后,有关如何使用TableColumn的更多详细信息,请参见{TableView}类文档。
@param <S> TableView泛型类型的类型(即S == TableView <S>)
@param <T> 此TableColumn中所有单元格中内容的类型。
@参照TableView
@参照TableCell
@参照TablePosition
@自JavaFX 2.0起
public class TableColumn<S,T> extends TableColumnBase<S,T> implements EventTarget {
/***************************************************************************
* *
* 静态属性和方法 *
* *
**************************************************************************/
/**
* 任何TableColumn编辑事件的父事件。
*/
@SuppressWarnings("unchecked")
public static <S,T> EventType<CellEditEvent<S,T>> editAnyEvent() {
return (EventType<CellEditEvent<S,T>>) EDIT_ANY_EVENT;
}
private static final EventType<?> EDIT_ANY_EVENT =
new EventType<>(Event.ANY, "TABLE_COLUMN_EDIT");
/**
* 表示用户已经执行了一些交互操作以启动编辑事件,或者已调用{TableView#edit(int,javafx.scene.control.TableColumn)}方法.
*/
@SuppressWarnings("unchecked")
public static <S,T> EventType<CellEditEvent<S,T>> editStartEvent() {
return (EventType<CellEditEvent<S,T>>) EDIT_START_EVENT;
}
private static final EventType<?> EDIT_START_EVENT =
new EventType<>(editAnyEvent(), "EDIT_START");
/**
* 表示编辑已被取消,这意味着不应返回数据对数据源进行任何更改。
*/
@SuppressWarnings("unchecked")
public static <S,T> EventType<CellEditEvent<S,T>> editCancelEvent() {
return (EventType<CellEditEvent<S,T>>) EDIT_CANCEL_EVENT;
}
private static final EventType<?> EDIT_CANCEL_EVENT =
new EventType<>(editAnyEvent(), "EDIT_CANCEL");
/**
* 表示用户已提交编辑,这意味着应该返回数据对数据源进行更改以反映新数据。
*/
@SuppressWarnings("unchecked")
public static <S,T> EventType<CellEditEvent<S,T>> editCommitEvent() {
return (EventType<CellEditEvent<S,T>>) EDIT_COMMIT_EVENT;
}
private static final EventType<?> EDIT_COMMIT_EVENT =
new EventType<>(editAnyEvent(), "EDIT_COMMIT");
/**
* 如果在TableColumn实例上未指定cellFactory,则默认情况下将使用此实例。
* 目前,如果{Cell#item item}是一个Node,它只是在{TableCell#graphicProperty()graphic}属性内呈现TableCell item属性,
* 或者如果它不为null,它只是调用toString(),从而将结果字符串设置在其中 {Cell#textProperty()text}属性。
*/
public static final Callback<TableColumn<?,?>, TableCell<?,?>> DEFAULT_CELL_FACTORY =
new Callback<TableColumn<?,?>, TableCell<?,?>>() {
@Override public TableCell<?,?> call(TableColumn<?,?> param) {
return new TableCell<Object,Object>() {
@Override protected void updateItem(Object item, boolean empty) {
if (item == getItem()) return;
super.updateItem(item, empty);
if (item == null) {
super.setText(null);
super.setGraphic(null);
} else if (item instanceof Node) {
super.setText(null);
super.setGraphic((Node)item);
} else {
super.setText(item.toString());
super.setGraphic(null);
}
}
};
}
};
/***************************************************************************
* *
* 构造函数 *
* *
**************************************************************************/
/**
* 创建具有默认单元格工厂,比较器和onEditCommit实现的默认TableColumn。
*/
public TableColumn() {
getStyleClass().add(DEFAULT_STYLE_CLASS);
setOnEditCommit(DEFAULT_EDIT_COMMIT_HANDLER);
// 监听Column列表,以确保统一宽度。
// 并设置列等级,以使所有子列都知道此TableColumn是其父级。
getColumns().addListener(weakColumnsListener);
tableViewProperty().addListener(observable -> {
// 将此tableView的所有子级中含有相同列的子集设置为相同的TableView
for (TableColumn<S, ?> tc : getColumns()) {
tc.setTableView(getTableView());
}
// 由于RT-22391,此代码已被注释掉,启用此选项后,父列将为null,这是不希望的
// // 将此列的父级设置为也具有此tableView
// if (getParentColumn() != null) {
// getParentColumn().setTableView(getTableView());
// }
});
}
/**
* 使用默认单元格工厂,比较器和onEditCommit实现创建一个TableColumn,将文本设置为提供的字符串。
* @param 将TableColumn放置在TableView中时显示的字符串。
*/
public TableColumn(String text) {
this();
setText(text);
}
/***************************************************************************
* *
* 监听器 *
* *
**************************************************************************/
private EventHandler<CellEditEvent<S,T>> DEFAULT_EDIT_COMMIT_HANDLER = t -> {
int index = t.getTablePosition().getRow();
List<S> list = t.getTableView().getItems();
if (list == null || index < 0 || index >= list.size()) return;
S rowData = list.get(index);
ObservableValue<T> ov = getCellObservableValue(rowData);
if (ov instanceof WritableValue) {
((WritableValue)ov).setValue(t.getNewValue());
}
};
private ListChangeListener<TableColumn<S,?>> columnsListener = c -> {
while (c.next()) {
// 更新TableColumn.tableView属性
for (TableColumn<S,?> tc : c.getRemoved()) {
// 修复问题RT-16978。
// 在TableColumnHeader中,在移动TableColumn之前将其添加,然后将其删除。
// 这意味着在很短的时间内tc被复制了,我们可以防止将tableview和parent列清空。
// 如果不这么做,在非常特殊的情况下,重新排序然后对另一列进行排序会使一列的整个内容无效。
if (getColumns().contains(tc)) continue;
tc.setTableView(null);
tc.setParentColumn(null);
}
for (TableColumn<S,?> tc : c.getAddedSubList()) {
tc.setTableView(getTableView());
}
updateColumnWidths();
}
};
private WeakListChangeListener<TableColumn<S,?>> weakColumnsListener =
new WeakListChangeListener<TableColumn<S,?>>(columnsListener);
/***************************************************************************
* *
* 实例变量 *
* *
**************************************************************************/
// 包含应嵌套在此列中的所有子列
private final ObservableList<TableColumn<S,?>> columns = FXCollections.<TableColumn<S,?>>observableArrayList();
/***************************************************************************
* *
* 属性 *
* *
**************************************************************************/
// --- TableView
/**
* 该TableColumn所属的TableView。
*/
private ReadOnlyObjectWrapper<TableView<S>> tableView = new ReadOnlyObjectWrapper<TableView<S>>(this, "tableView");
public final ReadOnlyObjectProperty<TableView<S>> tableViewProperty() {
return tableView.getReadOnlyProperty();
}
final void setTableView(TableView<S> value) { tableView.set(value); }
public final TableView<S> getTableView() { return tableView.get(); }
// --- 单元格值工厂
/**
* 需要设置单元格值工厂,以指定如何填充单个TableColumn中的所有单元格。
* 单元格值工厂是一个{Callback},它提供一个{CellDataFeatures}实例,并期望返回一个{ObservableValue}。
* 将在内部观察返回的ObservableValue实例,以允许立即更新该值以反映在显示上。
*
* 设置单元格值工厂的示例:
*/
lastNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
public ObservableValue<String> call(CellDataFeatures<Person, String> p) {
// p.getValue() 返回特定TableView行的Person实例
return p.getValue().lastNameProperty();
}
});
/**
* 一种常见的方法是要使用Java Bean中的单个值填充TableColumn中的单元格。
* 为了支持这种常见方案,提供了{PropertyValueFactory}类。
* 有关如何使用它的更多信息,请参考此类,但这里简要地介绍了如何使用PropertyValueFactory类简化上述用例:
*/
lastNameCol.setCellValueFactory(new PropertyValueFactory<Person,String>("lastName"));
/**
*
*/
private ObjectProperty<Callback<CellDataFeatures<S,T>, ObservableValue<T>>> cellValueFactory;
public final void setCellValueFactory(Callback<CellDataFeatures<S,T>, ObservableValue<T>> value) {
cellValueFactoryProperty().set(value);
}
public final Callback<CellDataFeatures<S,T>, ObservableValue<T>> getCellValueFactory() {
return cellValueFactory == null ? null : cellValueFactory.get();
}
public final ObjectProperty<Callback<CellDataFeatures<S,T>, ObservableValue<T>>> cellValueFactoryProperty() {
if (cellValueFactory == null) {
cellValueFactory = new SimpleObjectProperty<Callback<CellDataFeatures<S,T>, ObservableValue<T>>>(this, "cellValueFactory");
}
return cellValueFactory;
}
// --- 单元格工厂
/**
* 此列中所有单元格的单元格工厂。
* 单元格工厂负责为单个表列提供每个单元格中的数据。
* 默认情况下,TableColumn使用{#DEFAULT_CELL_FACTORY默认单元格工厂},但是可以用自定义实现代替,例如以不同的方式显示数据或支持编辑。
* 创建自定义单元格工厂的文档( 参见{Cell}和{TableView})。
*
* 最后,在这里提供一些预设的单元格工厂。
* {javafx.scene.control.cell} package.
*/
private final ObjectProperty<Callback<TableColumn<S,T>, TableCell<S,T>>> cellFactory =
new SimpleObjectProperty<Callback<TableColumn<S,T>, TableCell<S,T>>>(
this, "cellFactory", (Callback<TableColumn<S,T>, TableCell<S,T>>) ((Callback) DEFAULT_CELL_FACTORY)) {
@Override protected void invalidated() {
TableView<S> table = getTableView();
if (table == null) return;
Map<Object,Object> properties = table.getProperties();
if (properties.containsKey(TableViewSkinBase.RECREATE)) {
properties.remove(TableViewSkinBase.RECREATE);
}
properties.put(TableViewSkinBase.RECREATE, Boolean.TRUE);
}
};
public final void setCellFactory(Callback<TableColumn<S,T>, TableCell<S,T>> value) {
cellFactory.set(value);
}
public final Callback<TableColumn<S,T>, TableCell<S,T>> getCellFactory() {
return cellFactory.get();
}
public final ObjectProperty<Callback<TableColumn<S,T>, TableCell<S,T>>> cellFactoryProperty() {
return cellFactory;
}
// --- 排序类型
/**
* 用于声明此列(如果它是TableView.sortOrderObservableList的一部分)是否 应按升序或降序排序。
* 如果此列位于sortOrder ObservableList中,切换此属性将导致TableView中的排序顺序发生更改。
*/
private ObjectProperty<SortType> sortType;
public final ObjectProperty<SortType> sortTypeProperty() {
if (sortType == null) {
sortType = new SimpleObjectProperty<SortType>(this, "sortType", SortType.ASCENDING);
}
return sortType;
}
public final void setSortType(SortType value) {
sortTypeProperty().set(value);
}
public final SortType getSortType() {
return sortType == null ? SortType.ASCENDING : sortType.get();
}
// --- 开始编辑时
private ObjectProperty<EventHandler<CellEditEvent<S,T>>> onEditStart;
public final void setOnEditStart(EventHandler<CellEditEvent<S,T>> value) {
onEditStartProperty().set(value);
}
public final EventHandler<CellEditEvent<S,T>> getOnEditStart() {
return onEditStart == null ? null : onEditStart.get();
}
/**
* 当用户成功启动编辑时,将触发此事件处理程序。
*/
public final ObjectProperty<EventHandler<CellEditEvent<S,T>>> onEditStartProperty() {
if (onEditStart == null) {
onEditStart = new SimpleObjectProperty<EventHandler<CellEditEvent<S,T>>>(this, "onEditStart") {
@Override protected void invalidated() {
eventHandlerManager.setEventHandler(TableColumn.<S,T>editStartEvent(), get());
}
};
}
return onEditStart;
}
// --- 提交编辑时
private ObjectProperty<EventHandler<CellEditEvent<S,T>>> onEditCommit;
public final void setOnEditCommit(EventHandler<CellEditEvent<S,T>> value) {
onEditCommitProperty().set(value);
}
public final EventHandler<CellEditEvent<S,T>> getOnEditCommit() {
return onEditCommit == null ? null : onEditCommit.get();
}
/**
* 用户成功提交编辑后,将触发此事件处理程序。
*/
public final ObjectProperty<EventHandler<CellEditEvent<S,T>>> onEditCommitProperty() {
if (onEditCommit == null) {
onEditCommit = new SimpleObjectProperty<EventHandler<CellEditEvent<S,T>>>(this, "onEditCommit") {
@Override protected void invalidated() {
eventHandlerManager.setEventHandler(TableColumn.<S,T>editCommitEvent(), get());
}
};
}
return onEditCommit;
}
// --- 取消编辑时
private ObjectProperty<EventHandler<CellEditEvent<S,T>>> onEditCancel;
public final void setOnEditCancel(EventHandler<CellEditEvent<S,T>> value) {
onEditCancelProperty().set(value);
}
public final EventHandler<CellEditEvent<S,T>> getOnEditCancel() {
return onEditCancel == null ? null : onEditCancel.get();
}
/**
* 用户取消编辑单元格时将触发此事件处理程序。
*/
public final ObjectProperty<EventHandler<CellEditEvent<S,T>>> onEditCancelProperty() {
if (onEditCancel == null) {
onEditCancel = new SimpleObjectProperty<EventHandler<CellEditEvent<S, T>>>(this, "onEditCancel") {
@Override protected void invalidated() {
eventHandlerManager.setEventHandler(TableColumn.<S,T>editCancelEvent(), get());
}
};
}
return onEditCancel;
}
/***************************************************************************
* *
* Public API *
* *
**************************************************************************/
/** {@inheritDoc} */
@Override public final ObservableList<TableColumn<S,?>> getColumns() {
return columns;
}
/** {@inheritDoc} */
@Override public final ObservableValue<T> getCellObservableValue(int index) {
if (index < 0) return null;
// Get the table
final TableView<S> table = getTableView();
if (table == null || table.getItems() == null) return null;
// Get the rowData
final List<S> items = table.getItems();
if (index >= items.size()) return null; // Out of range
final S rowData = items.get(index);
return getCellObservableValue(rowData);
}
/** {@inheritDoc} */
@Override public final ObservableValue<T> getCellObservableValue(S item) {
// Get the factory
final Callback<CellDataFeatures<S,T>, ObservableValue<T>> factory = getCellValueFactory();
if (factory == null) return null;
// Get the table
final TableView<S> table = getTableView();
if (table == null) return null;
// Call the factory
final CellDataFeatures<S,T> cdf = new CellDataFeatures<S,T>(table, this, item);
return factory.call(cdf);
}
/***************************************************************************
* *
* 样式表处理 *
* *
**************************************************************************/
private static final String DEFAULT_STYLE_CLASS = "table-column";
/**
* {@inheritDoc}
* @return "TableColumn"
* @since JavaFX 8.0
*/
@Override
public String getTypeSelector() {
return "TableColumn";
}
/**
* {@inheritDoc}
* @return {@code getTableView()}
* @since JavaFX 8.0
*/
@Override
public Styleable getStyleableParent() {
return getTableView(); }
/**
* {@inheritDoc}
* @since JavaFX 8.0
*/
@Override
public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
return getClassCssMetaData();
}
/**
* @return 与此类关联的CssMetaData,可以包括其父类的CssMetaData。
* @since JavaFX 8.0
*/
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
return Collections.emptyList();
}
/**
* @treatAsPrivate 实现细节
* @deprecated 这是一个内部API,不可使用,将在下一版本中删除。
*/
@Deprecated
// SB-依赖性:已提交RT-21094进行跟踪
public Node impl_styleableGetNode() {
if (! (getTableView().getSkin() instanceof TableViewSkin)) return null;
TableViewSkin<?> skin = (TableViewSkin<?>) getTableView().getSkin();
TableHeaderRow tableHeader = skin.getTableHeaderRow();
NestedTableColumnHeader rootHeader = tableHeader.getRootHeader();
// 我们现在需要搜索标题。 我们将深度优先。
return scan(rootHeader);
}
private TableColumnHeader scan(TableColumnHeader header) {
// 首先测试父类是不是我们想要的东西
if (TableColumn.this.equals(header.getTableColumn())) {
return header;
}
if (header instanceof NestedTableColumnHeader) {
NestedTableColumnHeader parent = (NestedTableColumnHeader) header;
for (int i = 0; i < parent.getColumnHeaders().size(); i++) {
TableColumnHeader result = scan(parent.getColumnHeaders().get(i));
if (result != null) {
return result;
}
}
}
return null;
}
/***************************************************************************
* *
* 支持界面 *
* *
**************************************************************************/
/**
* TableColumn中用作包装类的支持类,以提供特定{Cell}的所有必需信息。
* 一旦实例化,该类是不可变的。
*
* @param <S> TableView类型
* @param <T> TableColumn类型
* @since JavaFX 2.0
*/
public static class CellDataFeatures<S,T> {
private final TableView<S> tableView;
private final TableColumn<S,T> tableColumn;
private final S value;
/**
* 使用给定属性设置为此实例的只读值来实例化CellDataFeatures实例。
*
* @param tableView 该实例引用的TableView。
* @param tableColumn 该实例引用的TableColumn。
* @param value TableView中一行的值。
*/
public CellDataFeatures(TableView<S> tableView,
TableColumn<S,T> tableColumn, S value) {
this.tableView = tableView;
this.tableColumn = tableColumn;
this.value = value;
}
/**
* 返回传递给构造函数的值。
*/
public S getValue() {
return value;
}
/**
* 返回传递给构造函数的{TableColumn}。
*/
public TableColumn<S,T> getTableColumn() {
return tableColumn;
}
/**
* 返回传递给构造函数的{TableView}。
*/
public TableView<S> getTableView() {
return tableView;
}
}
/**
* 用户在表单元格上执行编辑时触发的事件。
* @since JavaFX 2.0
*/
public static class CellEditEvent<S,T> extends Event {
private static final long serialVersionUID = -609964441682677579L;
/**
* 所有单元格编辑事件类型的通用父类型。
* @since JavaFX 8.0
*/
public static final EventType<?> ANY = EDIT_ANY_EVENT;
// 代表最终用户输入的新值。
// 这不是返回到TableView.items列表的值-此新值仅表示单个单元格的输入,
// 因此很可能需要返回到TableView.items列表中某个项目的属性。
private final T newValue;
// 编辑事件的位置
private transient final TablePosition<S,T> pos;
/**
* 创建一个新事件,随后可以将其触发给相关的侦听器。
*
* @param table 发生此事件的TableView。
* @param pos 此事件发生的位置。
* @param eventType 发生的事件的类型。
* @param newValue 最终用户输入的值。
*/
public CellEditEvent(TableView<S> table, TablePosition<S,T> pos,
EventType<CellEditEvent<S,T>> eventType, T newValue) {
super(table, Event.NULL_SOURCE_TARGET, eventType);
if (table == null) {
throw new NullPointerException("TableView can not be null");
}
this.pos = pos;
this.newValue = newValue;
}
/**
* 返回发生此事件的TableView。
* @return 发生此事件的TableView控件。
*/
public TableView<S> getTableView() {
return pos.getTableView();
}
/**
* 返回发生此事件的TableColumn。
*
* @return 进行编辑的TableColumn。
*/
public TableColumn<S,T> getTableColumn() {
return pos.getTableColumn();
}
/**
* 返回发生此事件的位置。
* @return 此事件发生的位置。
*/
public TablePosition<S,T> getTablePosition() {
return pos;
}
/**
* 返回最终用户输入的新值。
* 这不是要返回到TableView.items列表中的值-此新值仅表示单个单元格的输入,
* 因此可能需要返回到TableView.items列表中某个项目的属性。
*
* @return 一个对象,代表用户输入的新值。
*/
public T getNewValue() {
return newValue;
}
/**
* 尝试在{#getTablePosition()}返回的TablePosition中引用的位置处返回旧值。 由于多种原因,它可能返回null。
* @return 返回存储在正在编辑的位置中的值,或者为null
* 如果无法检索。
*/
public T getOldValue() {
S rowData = getRowValue();
if (rowData == null || pos.getTableColumn() == null) {
return null;
}
// 如果我们在这里,我们现在需要获取特定列的数据
return (T) pos.getTableColumn().getCellData(rowData);
}
/**
* 一种便捷方法,该方法为{#getTablePosition()}中返回的{TablePosition}中包含的行返回行值
* (即从TableView {TableView#itemsProperty()项目}列表中)。
*/
public S getRowValue() {
List<S> items = getTableView().getItems();
if (items == null) return null;
int row = pos.getRow();
if (row < 0 || row >= items.size()) return null;
return items.get(row);
}
}
/**
* 指定要应用于特定列的排序类型的枚举类型。
* @since JavaFX 2.0
*/
public static enum SortType {
/**
* 升序排序。
*/
ASCENDING,
/**
* 降序排列。
*/
DESCENDING;
// UNSORTED
}
}