Wrap your code from the insertUpdate(DocumentEvent e)
in SwingUtilities.invokeLater()
java.lang.IllegalStateException when updating JComboBox with setModel()?
-
23-07-2023 - |
質問
I was trying to updating JComboBox with setModel(). However, it throws a java.lang.IllegalStateException. Specifically, I used a JTextField to point to the Editable component of the JComboBox, and Update the JComboBox every time the JTextField recieves a new input.
Can anyone tell me the reason?
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) {
}
}
Here is the exceptions:
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)
解決
他のヒント
Doing this can produce some very weird results. The API is designed to protect against threaded updates and potential modifications that might cause infinite loops
In this case, the text field is modified, the Document
is updated, the DocumentListener
is notified and you try to change the field again, which would start the cycle again, except the Document
has a guard to prevent you from doing just this very thing, hence the Exception
.
What you need to do is two things.
- You need to update the text field AFTER the
DocumentListener
has been notified. As has already been stated, this can be achieved by usingSwingUtilities.invokeLater
- Provide some kind of flag so you don't try and modify your field on events cause by you modifying the field due to
DocumentListener
events...confused yet?
Basically, this example uses SwingUtilities.invokeLater
to update the field AFTER the DocumentListener
has completed it's event notifications and a simple boolean
flag to provide a means to "ignore" updates that we have triggered...
It's messy, but it will get the job done...mostly...
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) {
}
}