我能否开发一个可编辑的JComboBox的动态下拉框,在用户输入或删除文本时根据输入动态更新内容?

编程语言 2026-07-10

我在用Java 17设置 JComboBox 的弹出框时遇到了一些问题。我在使用WindowBuilder开发我的应用,我希望弹出框在用户输入或删除文本时能更新,只显示与用户输入的文本相匹配的项。若字符串为空,弹出框会显示在原始菜单中;在所有情况下,菜单都可见。为此,我尝试在我的 comboBox 中通过 setModel() 方法来修改模型,在 JcomboBox 的内部的 jTextField 中放入一个 DocumentListener(我称它为editor),但出现了异常:

"Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Attempt to mutate in notification"

首先,我想去查我的老师/导师的指南,但没有关于动态 JcomboBox 的示例。我也在其他论坛上搜索过,甚至在这个论坛里,唯一能做的就是尝试用 MutableComboBoxModel,但连在Java中加载都失败,即使使用快捷键 Ctrl + shift + O(我使用 Eclipse)。我还尝试观看YouTube视频,但没有类似的内容。如果你能帮忙,我将不胜感激,我现在正在使用 Windows 11

以下是代码,位于 prove.java 文档中。

错误的跟踪和代码如下(无论是在点击 JcomboBox 还是在编辑 comboBox 时都会出现错误):

Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Attempt to mutate in notification
    at java.desktop/javax.swing.text.AbstractDocument.writeLock(AbstractDocument.java:1372)
    at java.desktop/javax.swing.text.AbstractDocument.replace(AbstractDocument.java:688)
    at java.desktop/javax.swing.text.JTextComponent.setText(JTextComponent.java:1725)
    at java.desktop/javax.swing.plaf.metal.MetalComboBoxEditor$1.setText(MetalComboBoxEditor.java:63)
    at java.desktop/javax.swing.plaf.basic.BasicComboBoxEditor.setItem(BasicComboBoxEditor.java:93)
    at java.desktop/javax.swing.JComboBox.configureEditor(JComboBox.java:1439)
    at java.desktop/javax.swing.plaf.basic.BasicComboBoxUI$Handler.propertyChange(BasicComboBoxUI.java:1812)
    at java.desktop/javax.swing.plaf.basic.BasicComboBoxUI$PropertyChangeHandler.propertyChange(BasicComboBoxUI.java:704)
    at java.desktop/javax.swing.plaf.metal.MetalComboBoxUI$MetalPropertyChangeListener.propertyChange(MetalComboBoxUI.java:224)
    at java.desktop/java.beans.PropertyChangeSupport.fire(PropertyChangeSupport.java:343)
    at java.desktop/java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:335)
    at java.desktop/java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:268)
    at java.desktop/java.awt.Component.firePropertyChange(Component.java:8717)
    at java.desktop/javax.swing.JComboBox.setModel(JComboBox.java:362)
    at cfgs.prove$3.removeUpdate(prove.java:136)
    at java.desktop/javax.swing.text.AbstractDocument.fireRemoveUpdate(AbstractDocument.java:285)
    at java.desktop/javax.swing.text.AbstractDocument.handleRemove(AbstractDocument.java:652)
    at java.desktop/javax.swing.text.AbstractDocument.remove(AbstractDocument.java:620)
    at java.desktop/javax.swing.text.AbstractDocument.replace(AbstractDocument.java:696)
    at java.desktop/javax.swing.text.JTextComponent.setText(JTextComponent.java:1725)
    at java.desktop/javax.swing.plaf.metal.MetalComboBoxEditor$1.setText(MetalComboBoxEditor.java:63)
    at cfgs.prove$4.mousePressed(prove.java:156)
    at java.desktop/java.awt.AWTEventMulticaster.mousePressed(AWTEventMulticaster.java:288)
    at java.desktop/java.awt.Component.processMouseEvent(Component.java:6618)
    at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3398)
    at java.desktop/java.awt.Component.processEvent(Component.java:6386)
    at java.desktop/java.awt.Container.processEvent(Container.java:2266)
    at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:4996)
    at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2324)
    at java.desktop/java.awt.Component.dispatchEvent(Component.java:4828)
    at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4948)
    at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4572)
    at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4516)
    at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2310)
    at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2780)
    at java.desktop/java.awt.Component.dispatchEvent(Component.java:4828)
    at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:775)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:98)
    at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:747)
    at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
    at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:744)
    at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
package cfgs;

import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import java.awt.Font;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.regex.Pattern;

import javax.swing.JComboBox;
import javax.swing.DefaultComboBoxModel;

public class prove extends JFrame {

    private static final long serialVersionUID = 1L;
    private JPanel contentPane;
    protected JComboBox<String> comboBox;
    protected static String[] usuarios = {"James", "Ana", "Andres", "Maria", "John", "gustavo"};
    protected static DefaultComboBoxModel<String> comb;

    public static String[] FiltrarUsuarios(String str) {
        int i = 1, N = usuarios.length;
        ArrayList<String> filtrados = new ArrayList<String>();
        Pattern filtro = Pattern.compile("^"+str+".*"); //coincide con lo que empieza con este string (str). 
        comb = new DefaultComboBoxModel<String>(usuarios);
        String elemento;
        String[] retorno;
        while (i<N) {
            elemento = comb.getElementAt(i);
            if (filtro.matcher(elemento).matches()) //colocar el objeto
                filtrados.add(elemento);
            i++;
        }
        N = filtrados.size();
        i = 1;              //reutilizo ambas variables i, N.
        retorno = new String[++N];
        retorno[0] = str;
        while (i<N) {
            retorno[i] = filtrados.get(i-1);
            i++;
        }
        return retorno;
    }
    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    prove frame = new prove("Introduzca el usuario", true);
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the frame.
     */
    public prove(String mensaje, boolean bool) {

        comb = new DefaultComboBoxModel<String>(usuarios);

        setTitle("Dynamic comboBox.");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(null);

        JLabel lblNewLabel = new JLabel("Dynamic JComboBox");
        lblNewLabel.setFont(new Font("Tahoma", Font.BOLD, 17));
        lblNewLabel.setHorizontalAlignment(SwingConstants.CENTER);
        lblNewLabel.setBounds(116, 11, 184, 39);
        contentPane.add(lblNewLabel);

        JLabel a_message = new JLabel(mensaje);
        a_message.setHorizontalAlignment(SwingConstants.CENTER);
        a_message.setFont(new Font("Tahoma", Font.BOLD, 14));
        a_message.setBounds(116, 83, 184, 39);
        contentPane.add(a_message);

        JComboBox<String> comboBox = new JComboBox<String>();
        comboBox.setModel(new DefaultComboBoxModel<String>(usuarios));
        comboBox.setVisible(bool);
        comboBox.setModel(comb);
        comboBox.setEditable(true);
        comboBox.setFont(new Font("Tahoma", Font.BOLD, 13));
        comboBox.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                new DefaultComboBoxModel<String>(usuarios);
                comboBox.setModel(comb);
                comboBox.setPopupVisible(false);
            }
        });
        JTextField editor = (JTextField) comboBox.getEditor().getEditorComponent(); //hecho un casteo a Jtextfield para acceder al contenido con el método getText().
        editor.getDocument().addDocumentListener(new DocumentListener()  {

            @Override
            public void insertUpdate(DocumentEvent e) {
                String src = editor.getText();
                String[] lista;
                System.out.println("el contenido es:"+src);

                lista = FiltrarUsuarios(src);
                comb = new DefaultComboBoxModel<String>(lista); //usuarios
                comboBox.setModel(comb);
                comboBox.setPopupVisible(false);
            }

            @Override
            public void removeUpdate(DocumentEvent e) {

                String src = editor.getText();
                String[] lista;

                System.out.println("Ahora es borrar contenido. El contenido es:"+src);
                if (src.equals("")) {
                    comb = new DefaultComboBoxModel<String>(usuarios);
                    comboBox.setModel(comb);
                    comboBox.setPopupVisible(false);
                } else {
                    lista = FiltrarUsuarios(src);
                    comb = new DefaultComboBoxModel<String>(lista); //usuarios
                    comboBox.setModel(comb);
                    comboBox.setPopupVisible(false);
                }
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                System.out.println("Formato cambiado");
            }
        });

        editor.addMouseListener(new MouseAdapter() {

            @Override
            public void mousePressed(MouseEvent e) {
                editor.setText("");
            }
        });
        comboBox.setBounds(116, 133, 184, 39);
        contentPane.add(comboBox);

    }
}

解决方案

似乎问题产生了 comboBox.setModel(comb);,因为它是在 DocumentListener() 内执行,而对模型的“更改”可能会生成另一个事件,该事件会再次执行另一个 Listener,并且它会尝试再运行一个 comboBox.setModel(comb);,以此类推,可能会造成无限循环。

它可能需要稍后再执行

SwingUtilities.invokeLater(() -> {
    comboBox.setModel(comb);
    comboBox.setPopupVisible(false);
});

            @Override
            public void insertUpdate(DocumentEvent e) {
                String src = editor.getText();
                String[] lista;
                System.out.println("el contenido es:"+src);

                lista = FiltrarUsuarios(src);
                comb = new DefaultComboBoxModel<String>(lista); //usuarios
                SwingUtilities.invokeLater(() -> {
                    comboBox.setModel(comb);
                    comboBox.setPopupVisible(false);
                });
            }

            @Override
            public void removeUpdate(DocumentEvent e) {

                String src = editor.getText();
                String[] lista;

                System.out.println("Ahora es borrar contenido. El contenido es:"+src);
                if (src.equals("")) {
                    comb = new DefaultComboBoxModel<String>(usuarios);
                    SwingUtilities.invokeLater(() -> {
                        comboBox.setModel(comb);
                        comboBox.setPopupVisible(false);
                    });
                } else {
                    lista = FiltrarUsuarios(src);
                    comb = new DefaultComboBoxModel<String>(lista); //usuarios
                    SwingUtilities.invokeLater(() -> {
                        comboBox.setModel(comb);
                        comboBox.setPopupVisible(false);
                    });
                }
            }
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章