如何将带有 TextFormatter 和 Listener 的 TextField 添加到 TableCell 中?
How to add TextField with TextFormatter and Listener into TableCell?
我有 Water
个带有 Integer
参数 value
的对象。如果尚未创建,则此参数可以为空,等于 null。值将存储为 Integer
,但在呈现时,转换为 Decimal
并为用户打印为 String
并应用 TextFormatter
。用户进行更改后,应将其转换回适当的 Integer
并存储在数据库中。
我的Water
对象:
private Integer value;
table的代码:
@FXML
private TableView<Water> waterTable;
@FXML
private TableColumn<Water, Integer> valueColumn;
@FXML
private void initialize(){
waterTable.setItems(FXCollections.observableArrayList(thisMonthWaterList));
//don't know how to implemeny here instead of this code cell with TextField
/*valueColumn.setCellValueFactory(cellData -> new
SimpleIntegerProperty(cellData.getValue().getAmount()).asObject());
valueColumn.setCellFactory(new Callback<TableColumn<Water,Integer>,
TableCell<Water,Integer>>() {
@Override
public TableCell<Water, Integer> call(TableColumn<Water, Integer> param) {
return new TableCell<Water, Integer>(){
@Override
protected void updateItem(Integer value, boolean empty){
super.updateItem(value, empty);
if(value==null || empty){
setText(null);
}else{
setText(value.toString());
}
}
};
}
});*/
}
这是 TextFormatter
的代码:
TextField.setTextFormatter(new TextFormatter<>(change -> {
int maxLength = 10;
if (change.isAdded()) {
if(change.getControlNewText().length()<=maxLength){
if (change.getText().contains(",")) {
change.setText(change.getText().replaceAll(",", "."));
}
change = change.getControlNewText().matches("^\d*(\.\d{0,1})?$") ? change : null;
}else{
if(change.getText().length()==1){
change = null;
}else{
int allowedLength = maxLength - change.getControlText().length();
change.setText(change.getText().substring(0, allowedLength));
}
}
}
return change;
}));
Listener
的代码:
TextField.focusedProperty().addListener(new FocusChangeListener(TextField, text -> {
if(text.isEmpty()){
TextField.setText(Water.getAreaFormat());
}else{
TextField.setText(Water.toString(TextField.getText()));
}
thisObject.setValue(Water.toInt(TextField.getText()));
WaterDA.update(thisObject);
}, text -> {
if(text.equals(Water.getWaterFormat())){
TextField.setText("");
}
}));
实际上,您可以使用 TextFormatter
使用 StringConverter
和 UnaryOperator
来处理转换和防止无效输入。以下代码假定您在 Water
class 和 amountProperty()
方法 returns 中有一个 ObjectProperty<Integer>
。
如果Water.toString(int)
或Water.toString(Integer)
方法不存在,您需要实现从int
到字符串的转换才能使下面的代码生效。
private static final StringConverter<Integer> VALUE_CONVERTER = new StringConverter<Integer>() {
@Override
public String toString(Integer object) {
return object == null ? Water.getAreaFormat() : Water.toString(object);
}
@Override
public Integer fromString(String string) {
return Water.toInt(string);
}
};
// filter copied unmodified from your code
private static final UnaryOperator<TextFormatter.Change> VALUE_FILTER = change -> {
int maxLength = 10;
if (change.isAdded()) {
if(change.getControlNewText().length() <= maxLength){
if (change.getText().contains(",")) {
change.setText(change.getText().replaceAll(",", "."));
}
change = change.getControlNewText().matches("^\d*(\.\d{0,1})?$") ? change : null;
} else {
if (change.getText().length() == 1){
change = null;
} else {
int allowedLength = maxLength - change.getControlText().length();
change.setText(change.getText().substring(0, allowedLength));
}
}
}
return change;
};
@FXML
private TableColumn<Water, Integer> valueColumn;
@FXML
private void initialize(){
waterTable.setItems(FXCollections.observableArrayList(thisMonthWaterList));
valueColumn.setCellValueFactory(cellData -> cellData.getValue().amountProperty());
valueColumn.setCellFactory(new Callback<TableColumn<Water, Integer>, TableCell<Water, Integer>>() {
@Override
public TableCell<Water, Integer> call(TableColumn<Water, Integer> param) {
return new TableCell<Water, Integer>() {
private final TextFormatter<Integer> formatter;
private final TextField textField;
{
textField = new TextField();
formatter = new TextFormatter<>(VALUE_CONVERTER, null, VALUE_FILTER);
textField.setTextFormatter(formatter);
formatter.valueProperty().addListener((o, oldValue, newValue) -> {
Water water = (Water) getTableRow().getItem();
if (!Objects.equals(water.getAmount(), newValue)) {
// update item and db, if value was modified
water.setAmount(newValue);
WaterDA.update(water);
}
});
}
@Override
protected void updateItem(Integer value, boolean empty){
super.updateItem(value, empty);
if (empty){
setGraphic(null);
} else {
setGraphic(textField);
formatter.setValue(value);
}
}
};
}
});
}
这假设您的 TableCell
应该始终在 "editing state" 中。如果不是这种情况,则需要在 startEdit
/cancelEdit
和 commitEdit
方法中实现状态更改。
我有 Water
个带有 Integer
参数 value
的对象。如果尚未创建,则此参数可以为空,等于 null。值将存储为 Integer
,但在呈现时,转换为 Decimal
并为用户打印为 String
并应用 TextFormatter
。用户进行更改后,应将其转换回适当的 Integer
并存储在数据库中。
我的Water
对象:
private Integer value;
table的代码:
@FXML
private TableView<Water> waterTable;
@FXML
private TableColumn<Water, Integer> valueColumn;
@FXML
private void initialize(){
waterTable.setItems(FXCollections.observableArrayList(thisMonthWaterList));
//don't know how to implemeny here instead of this code cell with TextField
/*valueColumn.setCellValueFactory(cellData -> new
SimpleIntegerProperty(cellData.getValue().getAmount()).asObject());
valueColumn.setCellFactory(new Callback<TableColumn<Water,Integer>,
TableCell<Water,Integer>>() {
@Override
public TableCell<Water, Integer> call(TableColumn<Water, Integer> param) {
return new TableCell<Water, Integer>(){
@Override
protected void updateItem(Integer value, boolean empty){
super.updateItem(value, empty);
if(value==null || empty){
setText(null);
}else{
setText(value.toString());
}
}
};
}
});*/
}
这是 TextFormatter
的代码:
TextField.setTextFormatter(new TextFormatter<>(change -> {
int maxLength = 10;
if (change.isAdded()) {
if(change.getControlNewText().length()<=maxLength){
if (change.getText().contains(",")) {
change.setText(change.getText().replaceAll(",", "."));
}
change = change.getControlNewText().matches("^\d*(\.\d{0,1})?$") ? change : null;
}else{
if(change.getText().length()==1){
change = null;
}else{
int allowedLength = maxLength - change.getControlText().length();
change.setText(change.getText().substring(0, allowedLength));
}
}
}
return change;
}));
Listener
的代码:
TextField.focusedProperty().addListener(new FocusChangeListener(TextField, text -> {
if(text.isEmpty()){
TextField.setText(Water.getAreaFormat());
}else{
TextField.setText(Water.toString(TextField.getText()));
}
thisObject.setValue(Water.toInt(TextField.getText()));
WaterDA.update(thisObject);
}, text -> {
if(text.equals(Water.getWaterFormat())){
TextField.setText("");
}
}));
实际上,您可以使用 TextFormatter
使用 StringConverter
和 UnaryOperator
来处理转换和防止无效输入。以下代码假定您在 Water
class 和 amountProperty()
方法 returns 中有一个 ObjectProperty<Integer>
。
如果Water.toString(int)
或Water.toString(Integer)
方法不存在,您需要实现从int
到字符串的转换才能使下面的代码生效。
private static final StringConverter<Integer> VALUE_CONVERTER = new StringConverter<Integer>() {
@Override
public String toString(Integer object) {
return object == null ? Water.getAreaFormat() : Water.toString(object);
}
@Override
public Integer fromString(String string) {
return Water.toInt(string);
}
};
// filter copied unmodified from your code
private static final UnaryOperator<TextFormatter.Change> VALUE_FILTER = change -> {
int maxLength = 10;
if (change.isAdded()) {
if(change.getControlNewText().length() <= maxLength){
if (change.getText().contains(",")) {
change.setText(change.getText().replaceAll(",", "."));
}
change = change.getControlNewText().matches("^\d*(\.\d{0,1})?$") ? change : null;
} else {
if (change.getText().length() == 1){
change = null;
} else {
int allowedLength = maxLength - change.getControlText().length();
change.setText(change.getText().substring(0, allowedLength));
}
}
}
return change;
};
@FXML
private TableColumn<Water, Integer> valueColumn;
@FXML
private void initialize(){
waterTable.setItems(FXCollections.observableArrayList(thisMonthWaterList));
valueColumn.setCellValueFactory(cellData -> cellData.getValue().amountProperty());
valueColumn.setCellFactory(new Callback<TableColumn<Water, Integer>, TableCell<Water, Integer>>() {
@Override
public TableCell<Water, Integer> call(TableColumn<Water, Integer> param) {
return new TableCell<Water, Integer>() {
private final TextFormatter<Integer> formatter;
private final TextField textField;
{
textField = new TextField();
formatter = new TextFormatter<>(VALUE_CONVERTER, null, VALUE_FILTER);
textField.setTextFormatter(formatter);
formatter.valueProperty().addListener((o, oldValue, newValue) -> {
Water water = (Water) getTableRow().getItem();
if (!Objects.equals(water.getAmount(), newValue)) {
// update item and db, if value was modified
water.setAmount(newValue);
WaterDA.update(water);
}
});
}
@Override
protected void updateItem(Integer value, boolean empty){
super.updateItem(value, empty);
if (empty){
setGraphic(null);
} else {
setGraphic(textField);
formatter.setValue(value);
}
}
};
}
});
}
这假设您的 TableCell
应该始终在 "editing state" 中。如果不是这种情况,则需要在 startEdit
/cancelEdit
和 commitEdit
方法中实现状态更改。