如何在 DocumentListener 为 运行 时删除 JTextField 的内容?
How to remove content of JTextField while DocumentListener is running?
对于以下代码,我得到一个 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("");
}
}
为什么我不能在 DocumentListener 处于活动状态时更改 TextField?
我试图在 TextField 设置为“ ”时删除 DocumentListener,但这根本没有帮助。
我知道之前有人问过一个非常相似的问题,但我没有得到那个答案...
谢谢
一般情况下,您不会——在使用 DocumentListener 收听文档时不会更改文档的状态。我知道的两种可能的解决方案:
- 从您的 Listener 中,将进行您希望进行的更改的代码放入 Runnable 中,然后通过调用
SwingUtilities.invokeLater(yourRunnable)
将 Runnable 排队到 Swing 事件线程中。这是无耻的恶作剧
- 好多了:不要使用 DocumentListener,而是使用 DocumentFilter,因为这种类型的侦听器适合在文本在组件中可视化之前对 Document 进行更改。
不相关的附带问题:您的代码显示出令人担忧的耦合度,因为您试图从侦听器中更改 特定 文本组件中的文本。 DocumentListeners 应该完全不知道它们所收听的文档的文本组件,实际上可以添加到多个文档中。
DocumentFilter 有 3 个方法需要重写并执行您期望它们执行的操作:您期望它们执行的操作:
insertString
: 在文档中插入一个字符串
remove
: 从文档中删除文本
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 做一个布尔测试,看它是否是 "good",如果是,调用 super 的方法,这里是 replace(...)
,传入所有参数。如果不是,如果 newText 未通过测试,那么我会从文档中删除所有文本并显示 JOptionPane。
所以在这个例子中,我使用这个作为我的测试方法:
private boolean isTextOk(String text) {
return !BAD_TEXTS.contains(text);
}
测试文本是否是任何不允许的字符串,此处 "...", " ", "oops", "OOPS"
,但它可以是您想要的任何字符串。同样,如果文本通过文本,调用super的方法,否则删除文本:
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);
}
}
对于以下代码,我得到一个 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("");
}
}
为什么我不能在 DocumentListener 处于活动状态时更改 TextField?
我试图在 TextField 设置为“ ”时删除 DocumentListener,但这根本没有帮助。 我知道之前有人问过一个非常相似的问题,但我没有得到那个答案...
谢谢
一般情况下,您不会——在使用 DocumentListener 收听文档时不会更改文档的状态。我知道的两种可能的解决方案:
- 从您的 Listener 中,将进行您希望进行的更改的代码放入 Runnable 中,然后通过调用
SwingUtilities.invokeLater(yourRunnable)
将 Runnable 排队到 Swing 事件线程中。这是无耻的恶作剧 - 好多了:不要使用 DocumentListener,而是使用 DocumentFilter,因为这种类型的侦听器适合在文本在组件中可视化之前对 Document 进行更改。
不相关的附带问题:您的代码显示出令人担忧的耦合度,因为您试图从侦听器中更改 特定 文本组件中的文本。 DocumentListeners 应该完全不知道它们所收听的文档的文本组件,实际上可以添加到多个文档中。
DocumentFilter 有 3 个方法需要重写并执行您期望它们执行的操作:您期望它们执行的操作:
insertString
: 在文档中插入一个字符串remove
: 从文档中删除文本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 做一个布尔测试,看它是否是 "good",如果是,调用 super 的方法,这里是 replace(...)
,传入所有参数。如果不是,如果 newText 未通过测试,那么我会从文档中删除所有文本并显示 JOptionPane。
所以在这个例子中,我使用这个作为我的测试方法:
private boolean isTextOk(String text) {
return !BAD_TEXTS.contains(text);
}
测试文本是否是任何不允许的字符串,此处 "...", " ", "oops", "OOPS"
,但它可以是您想要的任何字符串。同样,如果文本通过文本,调用super的方法,否则删除文本:
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);
}
}