I am trying to make TableView in which i can enter only integer values that too within range say 100 to 500. Anything different from this range should be discarded. I did implement it using TextFormatter but i got stuck in for lower limit.
UnaryOperator<Change> flowFilter = change -> {
String controlNewText = change.getControlNewText();
String text = change.getText();
System.out.println("controlNetText : "+controlNewText);
System.out.println("text : "+text);
if(change.getControlNewText().isEmpty()) {
System.out.println("empty....");
return change;
} else if (controlNewText.matches("\\d*(\\.\\d*)?")) {
int val = Integer.parseInt(controlNewText);
//this below logic is failing at
// val >= MIN_RANGE_VALUE
if( val >= MIN_RANGE_VALUE && val <= MAX_RANGE_VALUE) {
System.out.println("min max value .....");
return change;
}
}
return null;
};
I got the answer or maybe something which works fine for me. Let me explain what I wanted. I wanted to create a TableView
with features of committing the editing TableCell
when focused is lost. I used this link to have my own custom TableCell
.
Also, I needed that user can input only values in a particular range say 100 to 500. I used JavaFX TextFormatter
class for that. With the help of TextFormatter
, I was able to filter user input so that only Integer or Float values are allowed. I was also able to put upper range limit (here 500) with the help of TextFormatter
. The real issue came with lower range limit because you cannot predict users mind. Say, if user types "12" and our range is 100 - 500, then I cannot predict whether user will commit it or will type more. This was the real problem. This lower range limit problem cannot be solved with TextFormatter
.
A comment suggested to put the checking logic in commitEdit()
method. I did it and it worked for me. What the below code does is that if user enters any value within range 100-500, it is accepted. Otherwise the recent old committed value remain in TableCell
of the TableView
. Below is the code for Custom TableCell
:
class CustomTableCell<S, T> extends TableCell<S, T> {
private final TextField textField = new TextField();
private final StringConverter<T> converter;
private final TextFormatter<T> textFormatter;
int MIN_RANGE_VALUE = 100;
int MAX_RANGE_VALUE = 500;
int MAX_DIGITS = 3;
public CustomTableCell(StringConverter<T> converter) {
this.converter = converter;
itemProperty().addListener((obsVal, oldItem, newItem) -> {
if(newItem == null) {
setText(null);
} else {
setText(converter.toString(newItem));
}
});
setGraphic(textField);
setContentDisplay(ContentDisplay.TEXT_ONLY);
textFormatter = new TextFormatter<>(flowFilter);
textField.setTextFormatter(new TextFormatter<>(flowFilter));
textField.setOnAction(event -> {
System.out.println("textField setOnAction called....");
commitEdit(this.converter.fromString(textField.getText()));
});
textField.focusedProperty().addListener((obsVal, wasFocused, isNowFocused) -> {
if(!isNowFocused) {
System.out.println("focused lost.....");
System.out.println("textField.getText() : " +textField.getText());
commitEdit(this.converter.fromString(textField.getText()));
}
});
textField.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if(event.getCode() == KeyCode.ESCAPE) {
textField.setText(converter.toString(getItem()));
cancelEdit();
event.consume();
} else if(event.getCode() == KeyCode.RIGHT) {
getTableView().getSelectionModel().selectRightCell();
event.consume();
} else if(event.getCode() == KeyCode.LEFT) {
getTableView().getSelectionModel().selectLeftCell();
event.consume();
} else if(event.getCode() == KeyCode.UP) {
getTableView().getSelectionModel().selectAboveCell();
event.consume();
} else if(event.getCode() == KeyCode.DOWN) {
getTableView().getSelectionModel().selectBelowCell();
event.consume();
}
});
}
UnaryOperator<Change> flowFilter = change -> {
String controlNewText = change.getControlNewText();
String text = change.getText();
System.out.println("controlNetText : "+controlNewText);
System.out.println("text : "+text);
if(change.getControlNewText().isEmpty()) {
System.out.println("empty returned....");
return change;
} else if (controlNewText.matches("\\d*") && controlNewText.length() <= MAX_DIGITS) {
int val = Integer.parseInt(controlNewText);
if( val <= MAX_RANGE_VALUE) {
System.out.println("max min range returned...");
return change;
}
}
System.out.println("textFormatter null returned....");
return null;
};
public static final StringConverter<String> IDENTITY_CONVERTER = new StringConverter<String>() {
@Override
public String toString(String object) {
return object;
}
@Override
public String fromString(String string) {
return string;
}
};
//Convenience method for creating an EditCell for a String value
public static CustomTableCell<ReceipeDataModel, Number> createStringCustomTableCell() {
return new CustomTableCell<ReceipeDataModel, Number>(new NumberStringConverter());
}
//set the text of TextField and display graphic
@Override
public void startEdit() {
super.startEdit();
textField.setText(converter.toString(getItem()));
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.requestFocus();
}
//revert to text display
@Override
public void cancelEdit() {
super.cancelEdit();
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
//commits the edit. Update the property if possible and revert to text display
@Override
public void commitEdit(T item) {
//below code for empty string and converter returns null
if(item == null) {
item = getItem();
}
//below code for putting range limits
if(item != null) {
long val = (long)item;
System.out.println("inside min max range if.....");
item = (val >= MIN_RANGE_VALUE && val <= MAX_RANGE_VALUE) ? item : getItem();
}
//this block is necessary because by deafult mechanism return false for isEditng() method when focus is lost.
//By Default, Only when we click on same TableRow, does the lost focus commits, not when we click outside the tableRow
if(item != null && ! isEditing() && !item.equals(getItem())) {
TableView<S> table = getTableView();
if(table != null) {
TableColumn<S, T> col = getTableColumn();
TablePosition<S, T> pos = new TablePosition<>(table, getIndex(), col);
CellEditEvent<S, T> cellEditEvent = new CellEditEvent<>(table, pos, TableColumn.editCommitEvent(), item);
Event.fireEvent(col, cellEditEvent);
}
}
super.commitEdit(item);
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}