JavaFX:'禁用'TableView行和列
我有一个TableView并希望允许用户禁用单独的行和列。JavaFX:'禁用'TableView行和列
表格数据是由API提供的,所以行和列的确切数量在运行时间之前是未知的,但通常是4-30行和15-25列。理想情况下,第一列/行将是一个标题,最后一行将是页脚(即:上面/侧面数据的摘要)。一旦用户执行了一些其他操作,他们应该能够禁用行或列 - 最好是通过单击标题 - 并从摘要中删除这些数据。
行/列仍应可见。排序不是必需的。编辑不是必需的。
我知道JavaFX只支持列标题,而不是行标题,当然也不支持页脚。这使我认为最好的选择是:
- Remove the TableView headers完全。
- 将第一个和最后一个列/行设置为页眉/页脚。
- setCellSelectionEnabled为true
- 如果所选单元格是标题单元格,请“禁用”该行或列;可能是通过CSS样式的组合并从摘要中删除单元格值。
- 如果所选单元格不是标题单元格,请执行其他操作。
- 如果再次单击标题单元格,请重新启用行或列。
这是最好的方法吗?有没有第三方库可以让工作更轻松?它甚至值得一起倾销TableView并使用GridPane?
谢谢。
正如@杰克斯指出的那样,似乎并没有一个捷径。所以这里是我解决问题的方法。
首先,我们hide the table header并允许单独选择单元格。 updateTable()
方法每n秒;它模拟了一个ScheduledService
API调用。 MyRow
对象只是一个ObservableList<TableCell>
的包装器,我们使用它,因为我们不知道有多少列会在运行时出现。
COLUMN_HEADER, ROW_FOOTER
等是枚举。
private void updateTable() {
Pane header = (Pane) myTable.lookup("TableHeaderRow");
header.setVisible(false);
myTable.getSelectionModel().setCellSelectionEnabled(true);
myTable.setLayoutY(-header.getHeight());
myTable.autosize();
List<MyRow> list = new CopyOnWriteArrayList();
Integer maxRows = 5, maxColumns = 5; // For example
MyRow row;
for (int rowId = 0; rowId <= maxRows; rowId++) {
Boolean isEmpty = myTable.getColumns().isEmpty();
row = new MyRow();
Integer total = 0;
for (int colId = 0; colId <= maxColumns; colId++) {
if (isEmpty) {
myTable.getColumns().add(createReactiveColumn());
}
TableCell cell = new MyTableCell();
if (rowId == 0 && colId == 0) {
// Top-left cell
} else if (rowId == 0) {
// Column title
cell.setUserData(COLUMN_HEADER);
cell.setText("CH" + Integer.toString(colId));
} else if (colId == 0) {
// Row title
cell.setUserData(ROW_HEADER);
cell.setText("RH" + Integer.toString(rowId));
} else if (colId == maxColumns) {
// Row 'footer'
cell.setUserData(ROW_FOOTER);
cell.setText(Integer.toString(total));
} else if (rowId == maxRows) {
// Column 'footer'
cell.setUserData(COLUMN_FOOTER);
cell.setText("CF" + Integer.toString(rowId));
} else {
// Data
Integer r = new Random().nextInt(9); // Simulate API data
if (!this.disabledColumns.contains(myTable.getColumns().get(colId))) {
total += r; // Sum of each enabled cell
}
cell.setUserData(DATA);
cell.setText(Integer.toString(r));
}
row.add(cell);
}
list.add(row);
}
myTable.getItems().setAll(list);
}
的TableColumn
只是采用setCellFactory()
方法:
private TableColumn<MyRow, String> createReactiveColumn() {
TableColumn<MyRow, String> column = new TableColumn<MyRow, String>();
column.setCellFactory(new Callback<TableColumn<MyRow, String>, TableCell<MyRow, String>>() {
@Override
public TableCell<MyRow, String> call(TableColumn<MyRow, String> param) {
return new MyTableCell();
}
});
column.setSortable(FALSE);
column.setMinWidth(40d);
return column;
}
MyTableCell
增加了MOUSE_CLICKED
事件处理程序,以及复制从更新TableCell
文本。
private class MyTableCell extends TableCell<MyRow, String> {
Boolean hasEventHandler = FALSE;
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty && getTableRow() != null && getTableRow().getItem() != null) {
MyRow er = (MyRow) getTableRow().getItem();
TableCell cell = er.get(getTableView().getColumns().indexOf(getTableColumn()));
this.setText(cell.getText());
if (cell.getUserData() instanceof MyTableCellEnum) {
MyTableCellEnumcellType = (MyTableCellEnum) cell.getUserData();
if (null != cellType && !hasEventHandler) {
switch (cellType) {
case COLUMN_HEADER:
addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
toggleColumn(getTableColumn());
}
});
hasEventHandler = TRUE;
break;
case ROW_HEADER:
addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
toggleRow(getTableRow());
}
});
hasEventHandler = TRUE;
break;
case DATA:
addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
// Do other action on selection
}
});
break;
default:
break;
}
}
}
}
}
}
最后,拨动方法只是换一个集残疾人TableRow
/TableColumn
对象之间的行/列,并更新CSS:
private void toggleRow(TableRow tableRow) {
if (this.disabledRows.contains(tableRow)) {
this.disabledRows.remove(tableRow);
tableRow.getStyleClass().remove("cell-disabled");
} else {
this.disabledRows.add(tableRow);
tableRow.getStyleClass().add("cell-disabled");
}
}
private void toggleColumn(TableColumn tableColumn) {
System.out.println(tableColumn);
if (this.disabledColumns.contains(tableColumn)) {
this.disabledColumns.remove(tableColumn);
tableColumn.getStyleClass().remove("cell-disabled");
} else {
this.disabledColumns.add(tableColumn);
tableColumn.getStyleClass().add("cell-disabled");
}
}
它的工作原理,我主要是喜欢它。包含hasEventHandler
布尔值并不理想:它看起来有点冒险,但我无法找到任何其他注册事件处理程序的方法,只能确保其实际工作。
欢迎提出意见和改进建议。如果有更好的想法,我会在接受我自己的答案前几天离开它。
你看过函数'setRowFactory'(来自TableView)和'setCellFactory'(来自TableColumn)吗?网上有很多例子,它允许程序员通过设置监听器来定制单元/列行为。由此,例如,如果点击列/单元格,则可以执行操作。 – Jacks
我见过这些。我的问题可能应该包括对他们的引用,因为如果没有某种捷径,那么这两种方法可能是必不可少的。 – AJR
据我所知,没有真正的“捷径”。只要举一个这些函数的例子并重写'call()'函数就可以根据需要创建单元格或列。您还可以在设置工厂时添加监听器,以便在您的单元格(或单元格内的单元格,或者单元格内的单元)被单击时触发某个操作。希望这有助于,如果没有,我可以给你一个小例子。 – Jacks