В общем случае вы не изменяете состояние документа, слушая его при использовании 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);
}
}