TreeTableView: установка строки недоступна для редактирования
Я хочу контролировать стилизацию некоторых строк TreeTableView на основе уровня в дереве. я использовал setRowFactory
и применить стиль, если эта строка является частью дочерних элементов первого уровня корня таблицы. Стиль работает хорошо, но я также хочу отключить установку флажка для этих строк. я смогу setDisable(true)
но это также отключает расширение TreeItem и SetEditable(false)
кажется, не имеет никакого эффекта.
РЕДАКТИРОВАТЬ: Я понимаю, что таблица должна быть редактируемой, тогда столбцы по умолчанию редактируемые. Но если я установлю TreeTableRow.setEditable(true);
или же TreeTableRow.setEditable(false);
Я никогда не вижу никакого эффекта. Кажется, описание setEditable кажется именно тем, что я хочу, но я не могу использовать его таким образом.
void javafx.scene.control.Cell.setEditable (логическое значение arg0)
setEditable public final void setEditable (логическое значение)
Позволяет определенным ячейкам не иметь возможности редактировать. Это полезно в случаях, когда, скажем, у List есть "строки заголовка" - не имеет смысла, чтобы строки заголовка были редактируемыми, поэтому они должны иметь редактируемый набор tofalse. Параметры: value - Логическое значение, представляющее, является ли ячейка редактируемой или нет. Если> true, ячейка является редактируемой, и если она ложна, ячейка не может быть отредактирована.
Главный:
public class TreeTableViewRowStyle extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) throws Exception {
// create the treeTableView and colums
TreeTableView<Person> ttv = new TreeTableView<Person>();
TreeTableColumn<Person, String> colName = new TreeTableColumn<>("Name");
TreeTableColumn<Person, Boolean> colSelected = new TreeTableColumn<>("Selected");
colName.setPrefWidth(100);
ttv.getColumns().add(colName);
ttv.getColumns().add(colSelected);
ttv.setShowRoot(false);
ttv.setEditable(true);
// set the columns
colName.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
colSelected.setCellFactory(CheckBoxTreeTableCell.forTreeTableColumn(colSelected));
colSelected.setCellValueFactory(new TreeItemPropertyValueFactory<>("selected"));
ttv.setRowFactory(table-> {
return new TreeTableRow<Person>(){
@Override
public void updateItem(Person pers, boolean empty) {
super.updateItem(pers, empty);
boolean isTopLevel = table.getRoot().getChildren().contains(treeItemProperty().get());
if (!isEmpty()) {
if(isTopLevel){
setStyle("-fx-background-color:lightgrey;");
setEditable(false); //THIS DOES NOT SEEM TO WORK AS I WANT
//setDisable(true); //this would disable the checkbox but also the expanding of the tree
}else{
setStyle("-fx-background-color:white;");
}
}
}
};
});
// creating treeItems to populate the treetableview
TreeItem<Person> rootTreeItem = new TreeItem<Person>();
TreeItem<Person> parent1 = new TreeItem<Person>(new Person("Parent 1"));
TreeItem<Person> parent2 = new TreeItem<Person>(new Person("Parent 1"));
parent1.getChildren().add(new TreeItem<Person>(new Person("Child 1")));
parent2.getChildren().add(new TreeItem<Person>(new Person("Child 2")));
rootTreeItem.getChildren().addAll(parent1,parent2);
ttv.setRoot(rootTreeItem);
// build and show the window
Group root = new Group();
root.getChildren().add(ttv);
stage.setScene(new Scene(root, 300, 300));
stage.show();
}
}
Модель лица:
public class Person {
private StringProperty name;
private BooleanProperty selected;
public Person(String name) {
this.name = new SimpleStringProperty(name);
selected = new SimpleBooleanProperty(false);
}
public StringProperty nameProperty() {
return name;
}
public BooleanProperty selectedProperty() {
return selected;
}
public void setName(String name){
this.name.set(name);
}
public void setSelected(boolean selected){
this.selected.set(selected);
}
}
2 ответа
Основная проблема заключается в том, что ни одна из редактируемых (или псевдоредактируемых, как CheckBoxXX) ячеек дерева / таблицы не учитывает возможность редактирования строки, в которой они содержатся. Я считаю это ошибкой.
Чтобы преодолеть это, вы должны расширить (псевдо) редактируемые ячейки и заставить их уважать редактируемые строки. Точная реализация отличается для псевдо- и реальных ячеек редактирования. Ниже приведены встроенные примеры, для частого использования вы должны сделать их на высшем уровне и использовать их повторно.
CheckBoxTreeTableCell: подкласс и переопределение updateItem для повторной привязки его отключенного свойства, например
colSelected.setCellFactory(c -> {
TreeTableCell cell = new CheckBoxTreeTableCell() {
@Override
public void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if (getGraphic() != null) {
getGraphic().disableProperty().bind(Bindings
.not(
getTreeTableView().editableProperty()
.and(getTableColumn().editableProperty())
.and(editableProperty())
.and(getTreeTableRow().editableProperty())
));
}
}
};
return cell;
});
Для реальной ячейки редактирования, fi TextFieldTreeTableCell: переопределяет startEdit и возвращает без вызова super, если строка не редактируется
colName.setCellFactory(c -> {
TreeTableCell cell = new TextFieldTreeTableCell() {
@Override
public void startEdit() {
if (getTreeTableRow() != null && !getTreeTableRow().isEditable()) return;
super.startEdit();
}
};
return cell;
});
Теперь вы можете переключать редактируемость строки, немного изменив логику, чтобы гарантировать полную очистку во всех случаях:
ttv.setRowFactory(table-> {
return new TreeTableRow<Person>(){
@Override
public void updateItem(Person pers, boolean empty) {
super.updateItem(pers, empty);
// tbd: check for nulls!
boolean isTopLevel = table.getRoot().getChildren().contains(treeItemProperty().get());
if (!isEmpty() && isTopLevel) {
// if(isTopLevel){
setStyle("-fx-background-color:lightgrey;");
setEditable(false);
}else{
setEditable(true);
setStyle("-fx-background-color:white;");
}
}
};
});
Вместо создания пользовательского TreeTableCell
подкласса, вы можете использовать следующий служебный метод, который в основном устанавливает новую фабрику ячеек в столбце, который делегирует исходную фабрику ячеек, но добавляет привязку возможности редактирования строки всякий раз, когда создается ячейка.
public <S, T> void bindCellToRowEditability(TreeTableColumn<S, T> treeTableColumn) {
// Keep a handle on the original cell-factory.
Callback<TreeTableColumn<S, T>, TreeTableCell<S, T>> callback = treeTableColumn.getCellFactory();
// Install a new cell-factory that performs the delegation.
treeTableColumn.setCellFactory(column -> {
TreeTableCell<S, T> cell = callback.call(column);
// Add a listener so that we pick up when a new row is set for the cell.
cell.tableRowProperty().addListener((observable, oldRow, newRow) -> {
// If the new row is non-null, we proceed.
if (newRow != null) {
// We get the cell and row editable-properties.
BooleanProperty cellEditableProperty = cell.editableProperty();
BooleanProperty rowEditableProperty = newRow.editableProperty();
// Bind the cell's editable-property with its row's property.
cellEditableProperty.bind(rowEditableProperty);
}
});
return cell;
});
}
Затем вы можете установить это для всех столбцов вашего TreeTableView как:
List<TreeTableColumn<S, ?>> columns = treeTableView.getColumns();
columns.forEach(this::bindCellToRowEditability);
Вам все еще нужен обычай TreeTableRow
который проверяет, является ли он верхним уровнем или нет, чтобы редактируемое значение было правильно установлено для самой строки. Однако установка редактируемого значения в строке теперь гарантирует, что все ячейки в этой строке правильно отражают редактируемое свойство строки.
Если вы хотите отключить определенную ячейку, тогда обрабатывайте логику отключения в CellFactory, а не в RowFactory. Статический метод forTreeTableColumn(..) - удобный метод для быстрого использования. Но это не единственный способ. Вы все еще можете создать свою собственную фабрику для CheckBoxTreeTableCell.
Так что вместо
colSelected.setCellFactory(CheckBoxTreeTableCell.forTreeTableColumn(colSelected));
установите клеточный завод, как показано ниже, и это должно работать для вас.
colSelected.setCellFactory(new Callback<TreeTableColumn<Person, Boolean>, TreeTableCell<Person, Boolean>>() {
@Override
public TreeTableCell<Person, Boolean> call(TreeTableColumn<Person, Boolean> column) {
return new CheckBoxTreeTableCell<Person, Boolean>(){
@Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
boolean isTopLevel = column.getTreeTableView().getRoot().getChildren().contains(getTreeTableRow().getTreeItem());
setEditable(!isTopLevel);
}
};
}
});