JavaFX从8迁移到21时表格单元格编辑丢失首击键的修复方案
问题:JavaFX 21可编辑表格首击键丢失(Java 8迁移后)
我们正从Java 8迁移至Java 21(包含JavaFX组件),迁移后可编辑表格出现严重问题:用户按Tab键进入单元格后开始输入,首击键会丢失,该功能在JavaFX 8中可正常工作。已知问题源于Java 11的事件流变更,尝试多种方案均无法稳定解决,恳请提供可行修复建议。
相关代码实现
public class EditableLongTableCell<T> extends TableCell<T,Long>{ private TextField textField; private final Pattern intPattern = Pattern.compile("-?\\d+"); private ChangeListener<? super Boolean> changeListener = (obs, ov, nv) -> { if (!nv) { if((intPattern.matcher(textField.getText()).matches())) { commitEdit(Long.parseLong((textField.getText()))); } } }; @Override public void startEdit() { if(editableProperty().get()){ if (!isEmpty()) { super.startEdit(); createTextField(); setText(null); setGraphic(textField); textField.requestFocus(); } } } @Override public void cancelEdit() { super.cancelEdit(); if(getItem()!=null && !StringUtility.isBlankOrNull(getItem().toString())) { setText(getItem().toString()); } else { setText(""); } setGraphic(null); } @Override public void updateItem(Long item, boolean empty) { super.updateItem(item, empty); if (empty) { setText(null); setGraphic(null); } else { if (isEditing()) { if (textField != null) { textField.setText(item.toString()); textField.selectAll(); } setText(null); setGraphic(textField); } else { if(item != null && !StringUtility.isBlankOrNull(item.toString())) { NumberFormat nf = NumberFormat.getNumberInstance(); nf.setMinimumFractionDigits(0); nf.setMaximumFractionDigits(0); setText(nf.format(item)); }else { setText(""); } setGraphic(null); } } } private void createTextField() { textField = new TextField(getString()); textField.addEventFilter(KeyEvent.KEY_TYPED, new TextFieldNumberFilter()); textField.setOnAction(evt -> { Long item = null; if(!StringUtility.isBlankOrNull(textField.getText())){ item = Long.parseLong((textField.getText())); } commitEdit(item); }); textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2); textField.focusedProperty().addListener(changeListener); textField.setOnKeyPressed((ke) -> { if (ke.getCode().equals(KeyCode.ESCAPE)) { textField.focusedProperty().removeListener(changeListener); cancelEdit(); } if (ke.getCode().equals(KeyCode.TAB)) { if(textField.getText() != null && !StringUtility.isBlankOrNull(textField.getText())) { commitEdit(Long.parseLong(textField.getText())); }else { commitEdit(null); } } if(ke.getCode().isLetterKey()){ ke.consume(); } }); } @Override public void commitEdit(Long item) { textField.focusedProperty().removeListener(changeListener); if (isEditing()) { super.commitEdit(item); } else { final TableView<T> table = getTableView(); if (table != null) { TablePosition<T, Long> position = new TablePosition<T, Long>(getTableView(), getTableRow().getIndex(), getTableColumn()); CellEditEvent<T, Long> editEvent = new CellEditEvent<T, Long>(table, position, TableColumn.editCommitEvent(), item); Event.fireEvent(getTableColumn(), editEvent); } updateItem(item, false); if (table != null) { table.edit(-1, null); } } } protected String getString() { return getItem() == null ? "" : getItem().toString(); }
修复建议
延迟焦点请求,避免事件抢占:JavaFX 11+中,Tab切换单元格的事件流会优先处理,直接调用
textField.requestFocus()可能无法让TextField真正捕获后续的输入事件。修改startEdit方法中的焦点请求代码,用Platform.runLater包裹:// 替换原textField.requestFocus(); Platform.runLater(() -> { textField.requestFocus(); textField.selectAll(); // 确保文本被选中,首击键直接替换内容 });检查事件过滤器是否消费了首击键:
TextFieldNumberFilter作为KEY_TYPED事件过滤器,可能误消费了首击的数字键。确保过滤器只消费非法输入,比如:class TextFieldNumberFilter implements EventHandler<KeyEvent> { @Override public void handle(KeyEvent event) { char c = event.getCharacter().charAt(0); // 只消费非数字、非负号的输入(负号只能在开头) if (!Character.isDigit(c) && !(c == '-' && textField.getText().isEmpty())) { event.consume(); } } }调整Tab键的处理逻辑:原代码中Tab键按下时直接提交编辑,这会干扰正常的焦点流转。改为在Tab键释放时提交,或者让焦点自然转移后再处理提交:
// 替换原Tab键的KeyPressed处理,改用KeyReleased textField.setOnKeyReleased((ke) -> { if (ke.getCode().equals(KeyCode.TAB)) { if(textField.getText() != null && !StringUtility.isBlankOrNull(textField.getText())) { commitEdit(Long.parseLong(textField.getText())); }else { commitEdit(null); } } });确保编辑状态与TextField状态同步:在
updateItem方法中,当处于编辑状态时,确保TextField的文本和选择状态正确设置,避免UI更新导致的焦点或选择丢失。
内容的提问来源于stack exchange,提问作者purring pigeon
相关产品推荐
相关产品推荐

