Как удалить содержимое JTextField во время работы DocumentListener? - PullRequest
0 голосов
/ 19 февраля 2020

для следующего кода я получаю исключение IllegalStateException (попытка изменить в уведомлении):

private class DocumentHandler implements DocumentListener {
    public void changedUpdate(DocumentEvent ev) {
        // unused
    }
    public void insertUpdate(DocumentEvent ev) {    
        if(textInput.getText().equals("...")) {
        JOptionPane.showMessageDialog(null, "...");
        textInput.setText("");
    }
}

Почему я не могу изменить TextField, когда активен DocumentListener?

Я пытался удалить DocumentListener, когда TextField установлен в «», но это не помогло вообще. Я знаю, что кто-то задавал очень похожий вопрос раньше, но я не получил этот ответ ...

Спасибо

1 Ответ

3 голосов
/ 19 февраля 2020

В общем случае вы не изменяете состояние документа, слушая его при использовании DocumentListener. Два возможных решения, о которых я знаю:

  • Внутри вашего слушателя поместите код, который вносит изменения, которые вы хотите sh, в Runnable и поставьте в очередь Runnable в поток событий Swing позвонив SwingUtilities.invokeLater(yourRunnable). Это бесстыдный клудж
  • Гораздо лучше: не используйте DocumentListener, а DocumentFilter, поскольку слушатель этого типа был ориентирован на внесение изменений в Document до визуализации текста внутри компонента.

Несвязанная побочная проблема: ваш код демонстрирует тревожную степень связности, когда вы пытаетесь изменить текст в Speci c текстовом компоненте изнутри вашего слушателя. DocumentListeners должны полностью соответствовать текстовому компоненту c, документ которого они слушают, и фактически могут быть добавлены к более чем одному документу.


DocumentFilter имеет 3 метода, которые необходимо переопределить и делать то, что вы ожидаете от них: что вы ожидаете от них:

  • insertString: вставить строку в документ
  • remove: удалить текст из document
  • replace: заменяет текст в документе

Более того, эти методы выполняют свои действия до , текстовый компонент отображает изменения в документе .

Таким образом, в переопределениях моего метода я извлекаю текст текущего документа и использую параметры для создания того, как будет выглядеть новый текст, например, для метода замены, который я сделал:

@Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs)
        throws BadLocationException {
    String currentText = fb.getDocument().getText(0, fb.getDocument().getLength());
    StringBuilder sb = new StringBuilder(currentText);

    String newText = sb.replace(offset, offset + length, text).toString();

Затем я выполняю логический тест newText, чтобы определить, «хорош» ли он, и, если это так, вызывает метод super, здесь replace(...), передавая все параметры. Если нет, то если newText не проходит тест, я удаляю весь текст из документа и показываю JOptionPane.

Так что в этом примере я использую его в качестве метода тестирования:

private boolean isTextOk(String text) {
    return !BAD_TEXTS.contains(text);
}

, который проверяет, является ли текст какой-либо из запрещенных строк, здесь "...", " ", "oops", "OOPS", но это могут быть любые строки, которые вы пожелаете. Опять же, если текст пропускает текст, вызовите метод супер, иначе удалите текст:

if (isTextOk(newText)) {
    super.replace(fb, offset, length, text, attrs);
} else {
    badText(fb);
}

Где badText(fb) делает:

private void badText(FilterBypass fb) throws BadLocationException {
    remove(fb, 0, fb.getDocument().getLength());
    JOptionPane.showMessageDialog(null, "Don't do this!", "Bad Text Entered",
            JOptionPane.WARNING_MESSAGE);
}

Весь пример :

import java.util.Arrays;
import java.util.List;
import javax.swing.*;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.text.PlainDocument;

@SuppressWarnings("serial")
public class ClearThreeDots extends JPanel {
    private JTextField textField = new JTextField(40);

    public ClearThreeDots() {
        ((PlainDocument) textField.getDocument()).setDocumentFilter(new MyDocFilter());
        add(textField);
    }

    private static void createAndShowGui() {
        ClearThreeDots mainPanel = new ClearThreeDots();

        JFrame frame = new JFrame("Clear Three Dots");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}
class MyDocFilter extends DocumentFilter {
    private static final List<String> BAD_TEXTS = Arrays.asList("...", "   ", "oops", "OOPS");

    @Override
    public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr)
            throws BadLocationException {
        String currentText = fb.getDocument().getText(0, fb.getDocument().getLength());
        StringBuilder sb = new StringBuilder(currentText);

        String newText = sb.insert(offset, string).toString();

        if (isTextOk(newText)) {
            super.insertString(fb, offset, string, attr);
        } else {
            badText(fb);
        }
    }

    @Override
    public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
        String currentText = fb.getDocument().getText(0, fb.getDocument().getLength());
        StringBuilder sb = new StringBuilder(currentText);

        String newText = sb.replace(offset, offset + length, "").toString();

        if (isTextOk(newText)) {
            super.remove(fb, offset, length);
        } else {
            badText(fb);
        }

    }

    @Override
    public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs)
            throws BadLocationException {
        String currentText = fb.getDocument().getText(0, fb.getDocument().getLength());
        StringBuilder sb = new StringBuilder(currentText);

        String newText = sb.replace(offset, offset + length, text).toString();

        if (isTextOk(newText)) {
            super.replace(fb, offset, length, text, attrs);
        } else {
            badText(fb);
        }

    }

    private boolean isTextOk(String text) {
        return !BAD_TEXTS.contains(text);
    }

    private void badText(FilterBypass fb) throws BadLocationException {
        remove(fb, 0, fb.getDocument().getLength());
        JOptionPane.showMessageDialog(null, "Don't do this!", "Bad Text Entered",
                JOptionPane.WARNING_MESSAGE);
    }

}
...