【问题标题】:java.lang.IllegalStateException when updating JComboBox with setModel()?使用 setModel() 更新 JComboBox 时出现 java.lang.IllegalStateException?
【发布时间】:2014-07-03 11:30:23
【问题描述】:

我试图用 setModel() 更新 JComboBox。但是,它会抛出 java.lang.IllegalStateException。 具体来说,我使用 JTextField 指向 JComboBox 的 Editable 组件,并在每次 JTextField 收到新输入时更新 JComboBox。

谁能告诉我原因?

package ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

public class TestFrame extends JFrame implements DocumentListener {

    private static final long serialVersionUID = 1L;

    // tool bar
    private JToolBar topToolBar;
    private JTextField wordSearchField;
    private JComboBox<String> wordSearchTips;

    // window size
    private static final int width = 700;
    private static final int height = 500;

    public TestFrame() {
    }

    public static void main(String[] argv) {
        new TestFrame().CreateUI();
    }

    public void CreateUI() {
        setPreferredSize(new Dimension(width, height));
        setResizable(false);
        setLayout(new BorderLayout());

        // bottom
        JPanel bottom = new JPanel();
        bottom.setPreferredSize(new Dimension(width, 480));
        bottom.setLayout(new BorderLayout());

        // top
        topToolBar = new JToolBar();
        topToolBar.setBackground(Color.WHITE);
        topToolBar.setPreferredSize(new Dimension(width, 30));

        wordSearchTips = new JComboBox<String>();
        wordSearchTips.setEditable(true);
        wordSearchTips.setSelectedIndex(-1);
        wordSearchField = (JTextField) wordSearchTips.getEditor()
                .getEditorComponent();
        wordSearchField.getDocument().addDocumentListener(this);

        topToolBar.add(wordSearchTips);

        add(topToolBar, BorderLayout.NORTH);
        add(bottom, BorderLayout.SOUTH);

        pack();
        setVisible(true);

    }

    @Override
    public void changedUpdate(DocumentEvent e) {

    }

    @Override
    public void insertUpdate(DocumentEvent e) {

        String keyword = wordSearchField.getText().trim();
        DefaultComboBoxModel<String> m = new DefaultComboBoxModel<String>();
        ;
        for (int i = 0; i < 10; i++) {
            m.addElement(i + "");
        }
        wordSearchTips.setModel(m);
        wordSearchTips.setSelectedIndex(-1);
        ((JTextField) wordSearchTips.getEditor().getEditorComponent())
                .setText(keyword);
        wordSearchTips.showPopup();
    }

    @Override
    public void removeUpdate(DocumentEvent e) {
    }
}

以下是例外情况:

Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Attempt to mutate in notification
    at javax.swing.text.AbstractDocument.writeLock(Unknown Source)
    at javax.swing.text.AbstractDocument.replace(Unknown Source)
    at javax.swing.text.JTextComponent.setText(Unknown Source)
    at javax.swing.plaf.metal.MetalComboBoxEditor$1.setText(Unknown Source)
    at javax.swing.plaf.basic.BasicComboBoxEditor.setItem(Unknown Source)
    at javax.swing.JComboBox.configureEditor(Unknown Source)
    at javax.swing.plaf.basic.BasicComboBoxUI$Handler.propertyChange(Unknown Source)
    at javax.swing.plaf.basic.BasicComboBoxUI$PropertyChangeHandler.propertyChange(Unknown Source)
    at javax.swing.plaf.metal.MetalComboBoxUI$MetalPropertyChangeListener.propertyChange(Unknown Source)
    at java.beans.PropertyChangeSupport.fire(Unknown Source)
    at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
    at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
    at java.awt.Component.firePropertyChange(Unknown Source)
    at javax.swing.JComboBox.setModel(Unknown Source)
    at ui.TestFrame.insertUpdate(TestFrame.java:94)
    at javax.swing.text.AbstractDocument.fireInsertUpdate(Unknown Source)
    at javax.swing.text.AbstractDocument.handleInsertString(Unknown Source)
    at javax.swing.text.AbstractDocument.insertString(Unknown Source)
    at javax.swing.text.PlainDocument.insertString(Unknown Source)
    at javax.swing.text.AbstractDocument.replace(Unknown Source)
    at javax.swing.text.JTextComponent.replaceSelection(Unknown Source)
    at javax.swing.text.DefaultEditorKit$DefaultKeyTypedAction.actionPerformed(Unknown Source)
    at javax.swing.SwingUtilities.notifyAction(Unknown Source)
    at javax.swing.JComponent.processKeyBinding(Unknown Source)
    at javax.swing.JComponent.processKeyBindings(Unknown Source)
    at javax.swing.JComponent.processKeyEvent(Unknown Source)
    at java.awt.Component.processEvent(Unknown Source)
    at java.awt.Container.processEvent(Unknown Source)
    at java.awt.Component.dispatchEventImpl(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.KeyboardFocusManager.redispatchEvent(Unknown Source)
    at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(Unknown Source)
    at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(Unknown Source)
    at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(Unknown Source)
    at java.awt.DefaultKeyboardFocusManager.dispatchEvent(Unknown Source)
    at java.awt.Component.dispatchEventImpl(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Window.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access$200(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue$4.run(Unknown Source)
    at java.awt.EventQueue$4.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)

【问题讨论】:

  • 不要尝试从DocumentListener 中修改JTextComponentDocument
  • DocumentListener 会在Document 被修改时收到通知,再次尝试修改它会导致再次通知DocumentListener,最终会导致StackOverFlowException跨度>
  • 我得到的不是StackOverFlowException,第一次进入函数好像失败了。
  • 这是因为系统旨在阻止您执行此操作,方法是在更新时锁定 Document 使其无法更新

标签: java swing jcombobox illegalstateexception


【解决方案1】:

将您的代码从insertUpdate(DocumentEvent e) 包装到SwingUtilities.invokeLater()

【讨论】:

  • 你介意告诉我原因或给我一些链接吗?
  • 我照你说的做了。它毫无例外地工作。但是,每次我在JCombobox中输入一个字符时,程序似乎进入了一个无限循环……
  • @flexwang 想一想。当文本字段被修改时,它会调用你的DocumentListener,然后你修改其中的字段,然后调用你的DocumentListener 和你好,无限循环......
  • @MadProgrammer 哦,我明白了。看来我最好实现一个 KeyAdapter 而不是 DocumentListener 以避免这种无限循环。
  • @flexwang 这不是我愿意这样做的方法,但考虑到DocumentListener 的问题,它可能是您唯一真正的解决方案。考虑看看在 SwingLabs 中的 AutoCompleteDecorator,SwingX 库,它做了类似的事情
【解决方案2】:

这样做会产生一些非常奇怪的结果。该 API 旨在防止线程更新和可能导致无限循环的潜在修改

在这种情况下,文本字段被修改,Document 被更新,DocumentListener 被通知并且您尝试再次更改该字段,这将再次开始循环,除了 Document 有一个保护为了防止你做这件事,因此Exception

你需要做的是两件事。

  1. 您需要在DocumentListener 收到通知后更新文本字段。如前所述,这可以通过使用SwingUtilities.invokeLater 来实现
  2. 提供某种标志,这样您就不会因为DocumentListener 事件而修改字段而尝试修改字段...感到困惑吗?

基本上,此示例使用SwingUtilities.invokeLaterDocumentListener 完成其事件通知后更新字段,并使用一个简单的boolean 标志来提供一种“忽略”我们触发的更新的方法...

它很乱,但它会完成工作......主要......

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

public class TestFrame extends JFrame implements DocumentListener {

    private static final long serialVersionUID = 1L;

    // tool bar
    private JToolBar topToolBar;
    private JTextField wordSearchField;
    private JComboBox<String> wordSearchTips;

    // window size
    private static final int width = 700;
    private static final int height = 500;

    public TestFrame() {
    }

    public static void main(String[] argv) {
        new TestFrame().CreateUI();
    }

    public void CreateUI() {
        setPreferredSize(new Dimension(width, height));
        setResizable(false);
        setLayout(new BorderLayout());

        // bottom
        JPanel bottom = new JPanel();
        bottom.setPreferredSize(new Dimension(width, 480));
        bottom.setLayout(new BorderLayout());

        // top
        topToolBar = new JToolBar();
        topToolBar.setBackground(Color.WHITE);
        topToolBar.setPreferredSize(new Dimension(width, 30));

        wordSearchTips = new JComboBox<String>();
        wordSearchTips.setEditable(true);
        wordSearchTips.setSelectedIndex(-1);
        wordSearchField = (JTextField) wordSearchTips.getEditor()
                .getEditorComponent();
        wordSearchField.getDocument().addDocumentListener(this);

        topToolBar.add(wordSearchTips);

        add(topToolBar, BorderLayout.NORTH);
        add(bottom, BorderLayout.SOUTH);

        pack();
        setVisible(true);

    }

    @Override
    public void changedUpdate(DocumentEvent e) {
    }

    boolean beenModified = false;

    @Override
    public void insertUpdate(DocumentEvent e) {

        if (!beenModified) {
            beenModified = true;

            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {

                    String keyword = wordSearchField.getText().trim();
                    DefaultComboBoxModel<String> m = new DefaultComboBoxModel<String>();
                    for (int i = 0; i < 10; i++) {
                        m.addElement(i + "");
                    }
                    wordSearchTips.setModel(m);
                    wordSearchTips.setSelectedIndex(-1);
                    ((JTextField) wordSearchTips.getEditor().getEditorComponent())
                            .setText(keyword);
                    wordSearchTips.showPopup();

                    beenModified = false;
                }
            });

        }
    }

    @Override
    public void removeUpdate(DocumentEvent e) {
    }
}

【讨论】:

  • @StanislavL 我讨厌自动完成组合框,很多事情都可能出错,需要考虑很多状态变化,更不用说ObjectString 转换...:P
  • 我确实喜欢在我的 IDE 中使用它们:-)
猜你喜欢
  • 1970-01-01
  • 2016-06-14
  • 2021-04-20
  • 1970-01-01
  • 2014-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多