【问题标题】:Can I make UndoManager consider DocumentFilter?我可以让 UndoManager 考虑 DocumentFilter 吗?
【发布时间】:2021-08-26 19:50:27
【问题描述】:

运行这个例子:

public class UndoRedoSSCCE extends JFrame {

    public UndoRedoSSCCE() {
        super("A");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        setLayout(new FlowLayout());

        JComboBox<String> comboBox = new JComboBox<>(new String[] { "letters & digits", "only digits" });
        JTextField textField = new JTextField(15);
        PlainDocument doc = new PlainDocument();
        textField.setDocument(doc);
        installUndoRedo(textField);

        comboBox.addActionListener(e -> {
            textField.setText("");
            if (comboBox.getSelectedIndex() == 0)
                doc.setDocumentFilter(new LettersAndDigitsFilter());
            else
                doc.setDocumentFilter(new OnlyDigitsFilter());
        });
        doc.setDocumentFilter(new LettersAndDigitsFilter());

        add(comboBox);
        add(textField);

        pack();
        setLocationByPlatform(true);
    }

    private static class LettersAndDigitsFilter extends DocumentFilter {
        @Override
        public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr)
                throws BadLocationException {
            StringBuilder sb = new StringBuilder();
            string.chars().filter(c -> Character.isDigit(c) || Character.isLetter(c))
                    .forEach(value -> sb.append((char) value));

            super.insertString(fb, offset, sb.toString(), attr);
        }

        @Override
        public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs)
                throws BadLocationException {
            StringBuilder sb = new StringBuilder();
            text.chars().filter(c -> Character.isDigit(c) || Character.isLetter(c))
                    .forEach(value -> sb.append((char) value));

            super.replace(fb, offset, length, sb.toString(), attrs);
        }
    }

    private static class OnlyDigitsFilter extends DocumentFilter {
        @Override
        public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr)
                throws BadLocationException {
            StringBuilder sb = new StringBuilder();
            string.chars().filter(Character::isDigit).forEach(value -> sb.append((char) value));

            super.insertString(fb, offset, sb.toString(), attr);
        }

        @Override
        public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs)
                throws BadLocationException {
            StringBuilder sb = new StringBuilder();
            text.chars().filter(Character::isDigit).forEach(value -> sb.append((char) value));

            super.replace(fb, offset, length, sb.toString(), attrs);
        }
    }

    public static void installUndoRedo(JTextComponent textComponent) {
        UndoManager undoManager = new UndoManager();
        textComponent.getDocument().addUndoableEditListener(undoManager);
        InputMap im = textComponent.getInputMap(JComponent.WHEN_FOCUSED);
        ActionMap am = textComponent.getActionMap();

        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_Z, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "Undo");
        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_Y, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "Redo");

        am.put("Undo", new AbstractAction() {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                if (undoManager.canUndo()) {
                    undoManager.undo();
                }
            }
        });
        am.put("Redo", new AbstractAction() {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                if (undoManager.canRedo()) {
                    undoManager.redo();
                }
            }
        });
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            new UndoRedoSSCCE().setVisible(true);
        });
    }
}

如果我输入包含字母和数字的文本并在组合框中选择“仅字母”,当我按下 control Z 时,这些字母和数字将出现在文本字段中。换句话说,UndoManager 会忽略文档过滤器。

看这个动图:

当我更改组合框选择时,文本变为空白。然后我按 CTRL+ Z 并在 OnlyDigits 过滤器打开时出现包含字母的文本。

我知道我可以undoManager.discardAllEdits,但是有可能让它工作吗?我的意思是在尝试重做/撤消时应用过滤器?

我也尝试@Override PlainDocument 中的一些方法,但它们也没有被调用以覆盖有意义的东西。

【问题讨论】:

    标签: java swing awt jtextfield


    【解决方案1】:

    撤消/重做应该恢复组件的状态而不是改变状态。

    我建议您在更改过滤器时应该:

    1. 保存当前文本,
    2. 清除文本字段中的文本
    3. UndoManager 上调用discardAllEdits()
    4. 一次一个字符地遍历旧文本并将该字符重新插入到文档中。这将允许在重建撤消/重做时过滤文本,就好像文本是使用当前过滤器输入的一样。

    【讨论】:

      猜你喜欢
      • 2013-10-19
      • 2019-08-30
      • 2012-11-09
      • 2020-09-26
      • 2011-12-21
      • 2018-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多