【问题标题】:Using undo and redo for JTextArea对 JTextArea 使用撤消和重做
【发布时间】:2010-03-30 17:55:13
【问题描述】:

我正在使用 Java swing 制作文本编辑器。我也在使用 JTextArea。我想知道如何在 JTextArea 中使用 Undo 和 Redo 功能,因为我无法使用它。

【问题讨论】:

    标签: java swing


    【解决方案1】:

    据我了解,JTextArea 没有内置的固有撤消/重做功能,但Google search 确实找到了this article,这可能会有所帮助。

    javax.swing 中显然存在一个Undo Manager,您可以将其连接到 JTextArea 的更改事件。

    【讨论】:

    • 这里有一个更完整示例的链接:java-forums.org/javax-swing/9570-undo-redo-jtextarea.html
    • @Petar 链接后面的代码无法处理选择所有文本并替换为剪贴板内容时发生的情况。发生的情况是文本区域被清空,而不是被其以前的内容替换。 Repro:(1)在剪贴板中放一些东西,(2)选择文本区域中的所有字符,(3)按^V将剪贴板粘贴到所选内容上。我还不知道发生了什么,但我打算找出答案。此答案中发布的链接背后的代码也是如此。我感觉问题出在“以前的内容”的定义上。
    • 问题是当您选择所有文本并按^V(粘贴)时,记录了两个事件而不是一个。第一个事件是删除所有事件,第二个是插入事件。
    • @PetarMinchev 答案是here,我们在这里看到了如何覆盖 PlainDocument 中的 replace 方法。
    【解决方案2】:

    你可以这样做

    UndoManager manager = new UndoManager();
    textArea.getDocument().addUndoableEditListener(manager);
    

    一旦管理器附加到 JTextArea 的文档中,它将监视所有更改 到文本区域的内容。

    将管理器附加到文本组件后,您必须提供一些方法来告诉 经理撤消/重做操作。

    在必要时调用 UndoManager 的 public void undo() 和 public void redo() 方法(例如 actionlistener 的 actionPerformed() 方法)

    您可以通过以下方式将 Action 对象附加到按钮上,而不是调用简化任务的 undo() 和 redo() 方法:

    JButton undoButton = new JButton(UndoManagerHelper.getUndoAction(manager));
    JButton redoButton = new JButton(UndoManagerHelper.getRedoAction(manager));
    

    【讨论】:

    • UndoManagerHelper 类用户定义的吗?我没有在 Eclipse 中自动完成它。我还搜索了 oracle java 文档,但找不到该类。我正在使用 java 8
    【解决方案3】:

    我已经有一段时间没有这样做了,我不记得细节了,但这里有一个包含一些信息的链接:http://java.sun.com/docs/books/tutorial/uiswing/components/generaltext.html

    向下滚动到标题为“侦听文档更改”的部分以开始使用。

    【讨论】:

    • +1,谁是反对这个的人??本教程有一个工作示例并解释了正在发生的事情,这肯定回答了这个问题。现在由 OP 阅读教程,实际上 OP 在发布此问题之前应该先阅读教程。事实上,如果用户在知道之前不了解 Swing 教程,那么他们可以访问有价值的参考资料,这些参考资料可以帮助解决未来的问题。这是所有问题都应该回答的方式。
    • 感谢您的讽刺言论camickr。感谢您帮助马歇尔。
    【解决方案4】:

    我必须通过多个链接才能获得足够的帮助。我在这里添加我成功实施的内容,只是为了帮助未来的访问者。我使用 JTextPane 实现了这一点,但假设同样适用于 JTextArea

        JTextArea textArea = new JTextArea();
        JButton undo = new JButton("Undo");
        JButton redo = new JButton("Redo");
        KeyStroke undoKeyStroke = KeyStroke.getKeyStroke(
                KeyEvent.VK_Z, Event.CTRL_MASK);
        KeyStroke redoKeyStroke = KeyStroke.getKeyStroke(
                KeyEvent.VK_Y, Event.CTRL_MASK);
    
        UndoManager undoManager = new UndoManager();
    
        Document document = textArea.getDocument();
        document.addUndoableEditListener(new UndoableEditListener() {
            @Override
            public void undoableEditHappened(UndoableEditEvent e) {
                undoManager.addEdit(e.getEdit());
            }
        });
    
        // Add ActionListeners
        undo.addActionListener((ActionEvent e) -> {
            try {
                undoManager.undo();
            } catch (CannotUndoException cue) {}
        });
        redo.addActionListener((ActionEvent e) -> {
            try {
                undoManager.redo();
            } catch (CannotRedoException cre) {}
        });
    
        // Map undo action
        textArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
                .put(undoKeyStroke, "undoKeyStroke");
        textArea.getActionMap().put("undoKeyStroke", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    undoManager.undo();
                 } catch (CannotUndoException cue) {}
            }
        });
        // Map redo action
        textArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
                .put(redoKeyStroke, "redoKeyStroke");
        textArea.getActionMap().put("redoKeyStroke", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    undoManager.redo();
                 } catch (CannotRedoException cre) {}
            }
        });
    

    【讨论】:

      【解决方案5】:

      我创建了一个简单的类,它可以通过单个方法调用将撤消功能分配给 JText 组件(JTextField、JTextArea 等):

      UndoTool.addUndoFunctionality(area);
      

      或者构造一个新的 JTextArea 并预先分配撤消功能:

      UndoTool.createJTextFieldWithUndo();
      

      这里是实用程序类的实现:

      public class UndoTool {
          private static final String REDO_KEY = "redo";
          private static final String UNDO_KEY = "undo";
      
          private JTextComponent component;
          private KeyStroke undo = KeyStroke.getKeyStroke("control Z");
          private KeyStroke redo = KeyStroke.getKeyStroke("control Y");
      
          public UndoTool(JTextComponent component) {
              this.component = component;
          }
      
          public void setUndo(KeyStroke undo) {
              this.undo = undo;
          }
      
          public void setRedo(KeyStroke redo) {
              this.redo = redo;
          }
      
          public static void addUndoFunctionality(JTextComponent component) {
              UndoTool tool = new UndoTool(component);
              UndoManager undo = tool.createAndBindUndoManager();
              tool.bindUndo(undo);
              tool.bindRedo(undo);
          }
      
          public static JTextArea createJTextAreaWithUndo() {
              JTextArea area = new JTextArea();
              addUndoFunctionality(area);
              return area;
          }
      
          public static JTextField createJTextFieldWithUndo() {
              JTextField field = new JTextField();
              addUndoFunctionality(field);
              return field;
          }
      
          public UndoManager createAndBindUndoManager() {
              Check.notNull(component);
      
              UndoManager manager = new UndoManager();
              Document document = component.getDocument();
              document.addUndoableEditListener(event -> manager.addEdit(event.getEdit()));
              return manager;
          }
      
          public void bindRedo(UndoManager manager) {
              component.getActionMap().put(REDO_KEY, new AbstractAction(REDO_KEY) {
                  @Override
                  public void actionPerformed(ActionEvent evt) {
                      try {
                          if (manager.canRedo()) {
                              manager.redo();
                          }
                      } catch (CannotRedoException ignore) {
                      }
                  }
              });
              component.getInputMap().put(redo, REDO_KEY);
          }
      
          public void bindUndo(UndoManager manager) {
              component.getActionMap().put(UNDO_KEY, new AbstractAction(UNDO_KEY) {
                  @Override
                  public void actionPerformed(ActionEvent evt) {
                      try {
                          if (manager.canUndo()) {
                              manager.undo();
                          }
                      } catch (CannotUndoException ignore) {
                      }
                  }
              });
              component.getInputMap().put(undo, UNDO_KEY);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-12-21
        • 2017-03-19
        • 2012-06-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多