带有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 中的无限更新循环?

Java中 EvenQueue.invokeLater用法

如何使用依赖:销毁而不造成无限循环

Swing JTextField 文本更改侦听器 DocumentListener 无限循环

在焦点侦听器中更改复合可绘制时,EditText会无限地触发焦点更改事件

如何保护C文件在ubuntu中进入无限循环