带有invokelater的文档监听器进入无限循环
Posted
技术标签:
【中文标题】带有invokelater的文档监听器进入无限循环【英文标题】:documentlistener with invokelater goes infinite loop 【发布时间】:2016-04-17 07:56:19 【问题描述】:我有一个带有文本字段的 Jpanel。我正在使用 documentListener 将更改保存为用户在文本字段中键入的内容。用户可以输入 1-1000 之间,如果他输入任何其他内容,则会弹出错误消息。 现在,我正在使用invokeLater,但如果用户输入> 1000,则会导致无限循环。我该如何解决这个问题。
mMaxLabelLength = new JTextField();
mMaxLabelLength.getDocument().addDocumentListener(this);
@Override
public void changedUpdate(DocumentEvent arg0)
@Override
public void insertUpdate(DocumentEvent arg0)
saveActions();
mMaxLabelLength.requestFocus();
@Override
public void removeUpdate(DocumentEvent arg0)
saveActions();
mMaxLabelLength.requestFocus();
private boolean saveActions()
// get text
String strValue = mMaxLabelLength.getText();
// validate: must be positive integer between 1-1000;
boolean bSaveIt = true;
try
int nValue = Integer.parseInt(strValue);
if (nValue < 1 || nValue > 1000)
bSaveIt = false;
catch (NumberFormatException ne)
bSaveIt = false;
// save data to properties if valid
if (bSaveIt)
//do something
else
// error message
JOptionPane.showMessageDialog(this, "Please enter an integer value between 1 and 1000.", "Invalid Entry", JOptionPane.INFORMATION_MESSAGE);
SwingUtilities.invokeLater(new Runnable()
@Override
public void run()
int nMaxLabel = getMaxPieLabel();
mMaxLabelLength.setText(new Integer(nMaxLabel).toString());
);
return false;
setVisible(true);
return true;
【问题讨论】:
在我看来,当您调用mMaxLabelLength.setText(...);
时,您正在触发 insertUpdate
... 再次调用 setText()
。
我理解(从我的日志中),mMaxLabelLength.setText(...) 同时调用 removeUpdate 和 insertUpdate ,这就是它进入无限循环的原因。随着 mMaxLabelLength.getText 变为 0。
它与“invokeAndWait”一起工作正常,但是当它尝试设置值时,我收到错误消息“无法从事件调度程序线程调用 invokeAndWait”
永远不要在 DocumentListener 中修改文档。也许您想使用 InputVerifier。
我确实尝试过使用 inputVerifier,但我遇到了同样的已知问题。当我将 inputVerifier 与 Jtabbedpanels 一起使用时。 ***.com/questions/34726674/…
【参考方案1】:
修改Document
的状态或与UI 交互并不是DocumentListener
的真正职责范围。
相反,您可能应该使用DocumentFilter
,这将允许您在提交到Document
之前捕获无效状态,并使用自定义事件通知来提醒相关方发生了违规行为。 ..
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
public class Example
public static void main(String[] args)
new Example();
public Example()
EventQueue.invokeLater(new Runnable()
@Override
public void run()
try
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex)
ex.printStackTrace();
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
);
public class TestPane extends JPanel
public TestPane()
setLayout(new GridBagLayout());
setBorder(new EmptyBorder(20, 20, 20, 20));
JTextField field = new JTextField(10);
LimitedRangeDocumentFilter filter = new LimitedRangeDocumentFilter(1, 1000);
filter.setLimitedRangeDocumentFilterListener(new LimitedRangeDocumentFilterListener()
@Override
public void updateWouldBeInvalid(LimitedRangeDocumentFilter filter, String text)
JOptionPane.showMessageDialog(TestPane.this,
text + " is not within " + filter.getMin() + "-" + filter.getMax() + " range",
"Error",
JOptionPane.ERROR_MESSAGE);
);
((AbstractDocument)field.getDocument()).setDocumentFilter(filter);
add(field);
public interface LimitedRangeDocumentFilterListener
public void updateWouldBeInvalid(LimitedRangeDocumentFilter filter, String text);
public class LimitedRangeDocumentFilter extends DocumentFilter
private int min;
private int max;
private LimitedRangeDocumentFilterListener listener;
public LimitedRangeDocumentFilter(int min, int max)
this.min = min;
this.max = max;
public int getMin()
return min;
public int getMax()
return max;
public void setLimitedRangeDocumentFilterListener(LimitedRangeDocumentFilterListener listener)
this.listener = listener;
@Override
public void insertString(DocumentFilter.FilterBypass fb, int offset,
String string, AttributeSet attr)
throws BadLocationException
StringBuilder sb = new StringBuilder(string);
for (int i = sb.length() - 1; i >= 0; i--)
char ch = sb.charAt(i);
if (!Character.isDigit(ch))
sb.deleteCharAt(i);
StringBuilder master = new StringBuilder(fb.getDocument().getText(0, fb.getDocument().getLength()));
master.insert(offset, sb.toString());
if (wouldBeValid(master.toString()))
super.insertString(fb, offset, sb.toString(), attr);
else if (listener != null)
listener.updateWouldBeInvalid(this, master.toString());
@Override
public void replace(DocumentFilter.FilterBypass fb,
int offset, int length, String string, AttributeSet attr) throws BadLocationException
if (length > 0)
fb.remove(offset, length);
insertString(fb, offset, string, attr);
protected boolean wouldBeValid(String text)
boolean wouldBeValid = false;
try
int value = Integer.parseInt(text);
if (value >= min && value <= max)
wouldBeValid = true;
catch (NumberFormatException exp)
return wouldBeValid;
请参阅Implementing a Document Filter 和DocumentFilter Examples 了解更多详情
【讨论】:
以上是关于带有invokelater的文档监听器进入无限循环的主要内容,如果未能解决你的问题,请参考以下文章
Swing JTextField 文本更改侦听器 DocumentListener 无限循环