多个可撤销JTextField实例仅最后一个响应撤销操作的问题
错误原因
代码存在的核心问题直接导致撤销功能仅对最后创建的实例生效:
- 快捷键绑定的触发条件选择错误:使用
JComponent.WHEN_IN_FOCUSED_WINDOW作为InputMap的作用域时,注册的快捷键为窗口级全局生效,不需要组件本身持有焦点就能触发。 - 三个文本框实例给同一组快捷键
Ctrl+Z/Ctrl+Y绑定了完全同名的ActionKey(undoKeyStroke/redoKeyStroke),Swing键绑定机制中,后注册的全局同名键绑定会覆盖先注册的绑定,因此最后创建的第三个文本框的撤销/重做动作会覆盖前两个实例的绑定,无论当前焦点在哪个输入框,触发快捷键时永远执行第三个实例绑定的撤销逻辑。
修复方案
将InputMap的作用域从JComponent.WHEN_IN_FOCUSED_WINDOW修改为JComponent.WHEN_FOCUSED即可,修改后只有当前获得焦点的文本框才会响应对应的撤销/重做快捷键,不会出现跨实例的绑定覆盖问题。
修正后的UndoableTextField构造方法代码如下:
public UndoableTextField() { UndoManager undoManager = new UndoManager(); getDocument().addUndoableEditListener(new UndoableEditListener() { @Override public void undoableEditHappened(UndoableEditEvent e) { undoManager.addEdit(e.getEdit()); } }); // 替换作用域为WHEN_FOCUSED,仅组件聚焦时响应快捷键 getInputMap(JComponent.WHEN_FOCUSED).put(undoKeyStroke, "undoKeyStroke"); getActionMap().put("undoKeyStroke", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { try { undoManager.undo(); } catch (CannotUndoException cue) {} } }); // 重做快捷键同步修改作用域 getInputMap(JComponent.WHEN_FOCUSED).put(redoKeyStroke, "redoKeyStroke"); getActionMap().put("redoKeyStroke", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { try { undoManager.redo(); } catch (CannotRedoException cre) {} } }); }
补充说明:如果需要保留窗口全局下聚焦组件响应快捷键的体验,也可以不修改作用域,给每个实例的ActionKey设置唯一值(比如拼接实例hashCode)避免同名绑定覆盖,但直接使用
WHEN_FOCUSED是文本编辑组件快捷键的标准实现方式,逻辑更简单也符合常规交互预期。
内容的提问来源于stack exchange,提问作者cdubbs
相关产品推荐
相关产品推荐

