You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多个可撤销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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 04:06:08