я играл с этим некоторое время. Суть в том, что метод commitEdit
по умолчанию в TableCell
, который вызывается при фиксации изменения текстового поля в TextFieldTableCell
, вызывает updateItem(...)
в ячейке таблицы с новым значением. Вот почему значение в ячейке меняется, даже если вы не меняете его в модели.
Чтобы предотвратить это, вам необходимо реализовать ячейку таблицы самостоятельно, что не слишком сложно. Простейшая реализация должна, вероятно, просто использовать ячейку таблицы текстового поля и переопределить updateItem(...)
, чтобы проверить правильность значения. Что-то вроде
tcAantalDagen.setCellFactory(col -> new TextFieldTableCell<T, Integer>(new IntegerStringConverter()) {
@Override
public void updateItem(Integer item, boolean empty) {
if (empty) {
super.updateItem(item, empty) ;
} else {
// if out of range, revert to previous value:
if (item.intValue() < 1 || item.intValue() > 31) {
item = getItem();
}
super.updateItem(item, empty);
}
}
});
должно работать (хотя я его не проверял). Очевидно, замените T
на любой тип элементов в таблице. Обратите внимание, что я использовал Integer
в качестве типа столбца, который вы можете сделать, если вы предоставите соответствующий конвертер для TextFieldTableCell
. Вы бы изменить значение ячейки завода в
tcAantalDagen.setCellValueFactory(cellData -> cellData.getValue().aantalDagenProperty().asObject());
Однако ... когда вы собираетесь все проблемы реализации ячейки таблицы, вы можете также предоставить тот, который просто не позволяет пользователю вводить недопустимое значение, которое намного приятнее для пользователя imho. Вы можете сделать это, создав текстовое поле для ячейки, которая использует TextFormatter
, которая просто наносит вето недействительные значения. Вы должны быть немного осторожны с ними, так как вы хотите разрешить частично редактируемые значения (так что в общем случае недостаточно только допустимых значений, поскольку они проверяются каждый раз, когда текст изменяется, а не только по фиксации). В этом случае единственное, что это означает, это то, что вы должны разрешить пустые строки в текстовом поле (иначе пользователь не сможет удалить текущее значение во время редактирования, что было бы неудобно). Вы можете использовать конвертер, чтобы вернуть текущее значение, если пользователь пытается зафиксировать пустую строку.
Так реализация этого может выглядеть
public class IntegerEditingCell extends TableCell<Item, Integer> {
private TextField textField ;
private TextFormatter<Integer> textFormatter ;
public IntegerEditingCell(int min, int max) {
textField = new TextField();
UnaryOperator<TextFormatter.Change> filter = c -> {
String newText = c.getControlNewText() ;
// always allow deleting all characters:
if (newText.isEmpty()) {
return c ;
}
// otherwise, must have all digits:
if (! newText.matches("\\d+")) {
return null ;
}
// check range:
int value = Integer.parseInt(newText) ;
if (value < min || value > max) {
return null ;
} else {
return c ;
}
};
StringConverter<Integer> converter = new StringConverter<Integer>() {
@Override
public String toString(Integer value) {
return value == null ? "" : value.toString() ;
}
@Override
public Integer fromString(String string) {
// if it's an int, return the parsed value
if (string.matches("\\d+")) {
return Integer.valueOf(string);
} else {
// otherwise, just return the current value of the cell:
return getItem() ;
}
}
};
textFormatter = new TextFormatter<Integer>(converter, 0, filter) ;
textField.setTextFormatter(textFormatter);
textField.addEventFilter(KeyEvent.KEY_RELEASED, e -> {
if (e.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
});
textField.setOnAction(e -> commitEdit(converter.fromString(textField.getText())));
textProperty().bind(Bindings
.when(emptyProperty())
.then((String)null)
.otherwise(itemProperty().asString()));
setGraphic(textField);
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
@Override
protected void updateItem(Integer value, boolean empty) {
super.updateItem(value, empty);
if (isEditing()) {
textField.requestFocus();
textField.selectAll();
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
} else {
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
@Override
public void startEdit() {
super.startEdit();
textFormatter.setValue(getItem());
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.requestFocus();
textField.selectAll();
}
@Override
public void commitEdit(Integer newValue) {
super.commitEdit(newValue);
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
@Override
public void cancelEdit() {
super.cancelEdit();
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
Это выглядит как много кода, но большинство из них просто создание текстового поля и форматирования, то есть некоторые довольно стандартная реализация клетка, просто убедитесь, что текстовое поле отображается в режиме редактирования, а обычный текст отображается в режиме без редактирования.
Теперь вам просто не нужно беспокоиться о проверке правильности ввода, поскольку пользователь не может ввести недопустимое значение.
Вот простой пример использования:
import java.util.Random;
import java.util.function.UnaryOperator;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class ValidatingTableColumn extends Application {
@Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView<>();
table.setEditable(true);
TableColumn<Item, String> nameColumn = new TableColumn<>("Item");
nameColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
TableColumn<Item, Integer> valueColumn = new TableColumn<>("Value");
valueColumn.setCellValueFactory(cellData -> cellData.getValue().valueProperty().asObject());
table.getColumns().add(nameColumn);
table.getColumns().add(valueColumn);
valueColumn.setCellFactory(col -> new IntegerEditingCell(1, 31));
valueColumn.setOnEditCommit(e -> {
table.getItems().get(e.getTablePosition().getRow()).setValue(e.getNewValue());
});
Random rng = new Random();
for (int i = 1; i <= 20; i++) {
table.getItems().add(new Item("Item "+i, rng.nextInt(31)+1));
}
Button debug = new Button("Show all values");
debug.setOnAction(e -> table.getItems().forEach(item -> System.out.println(item.getName()+" ("+item.getValue()+")")));
BorderPane.setAlignment(debug, Pos.CENTER);
BorderPane.setMargin(debug, new Insets(5));
BorderPane root = new BorderPane(table, null, null, debug, null);
primaryStage.setScene(new Scene(root, 600, 600));
primaryStage.show();
}
public static class Item {
private final IntegerProperty value = new SimpleIntegerProperty() ;
private final StringProperty name = new SimpleStringProperty();
public Item(String name, int value) {
setName(name);
setValue(value);
}
public final IntegerProperty valueProperty() {
return this.value;
}
public final int getValue() {
return this.valueProperty().get();
}
public final void setValue(final int value) {
this.valueProperty().set(value);
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
}
public static void main(String[] args) {
launch(args);
}
}
Что такая реализация у вас есть для вашей камеры? –
Эй, Джеймс, мой спаситель JavaFX! Я отредактирую свой основной пост, чтобы предоставить вам дополнительную информацию. – EarthMind
Мое редактирование, что вы просили? – EarthMind