【问题标题】:How to avoid firing actionlistener event of JComboBox when an item is get added into it dynamically in java?java - 当在java中动态添加项目时,如何避免触发JComboBox的actionlistener事件?
【发布时间】:2011-07-12 15:07:27
【问题描述】:

我需要您对以下任务的建议和指导。

我有一个框架,它有两个 JComboBox,假设它们被命名为 combo1 和 combo2,一个 JTable 和其他组件。

在初始阶段,当框架与上述组件可见时。 combo1 组合框填充了一些值,但初始阶段没有选择任何值,combo2 组合框被禁用并且表为空。

我在combo1 和combo2 上添加了一个actionListener。 combo1 中有两种类型的值,假设这些值是 type1 和 type2。

条件一: 当我们从 Combo1 中选择值 type1 时,会调用 combo1 的 actionListener 方法,该方法会调用一个使 combo2 保持禁用状态的方法,并将一些行添加到与从 combo1 中选择的值 type1 相关的表中。

条件 2: 当我们从 combo1 中选择值 type2 时,会调用 combo1 的 actionListener 方法,该方法调用一个方法,该方法使 combo2 填充与 type2 相关的一些值并启用,但没有从 combo2 中选择任何值,并且 table 也应该保持为空,直到我们从中选择任何值组合2。

表在每次向 combo2 添加值时,都会触发 combo2 的动作侦听器方法。在combo2 的actionListener 方法中,它获取combo2 选定的值,但这里没有combo2 的选定值,这会导致NullPointerException。

那么我应该怎么做才能使combo2的action listner方法在向combo2添加值后不会被执行。

【问题讨论】:

    标签: java swing jcombobox actionlistener


    【解决方案1】:

    为了避免 addItem 方法触发事件,最好在 JComboBox 中使用 DefaultComboBoxModel 来添加数据。此外,如果您调用 model.addElement(),则会触发一个事件,因此,您可以将所有元素添加到模型中,然后使用 JComboBox.setModel(model)。这样,如果您向模型添加元素,则不会触发事件,因为您尚未将 JComboBox 与模型链接。然后,我给你举个例子。

    private void rellenarArrendatarioComboBox(ArrayList<Arrendatario> arrendatarios) {
        DefaultComboBoxModel model = new DefaultComboBoxModel();
        model.addElement(new Arrendatario(" -- Seleccione un arrendatario --"));
        for (Arrendatario arrendatario : arrendatarios) {
            model.addElement(arrendatario);
        }
        ArrendatarioComboBox.setModel(model);
    }
    

    首先,我们创建模型,将所有元素添加到模型(不会触发事件,因为您没有将 JComboBox 与模型链接),我们使用 ArrendatarioComboBox.setModel(model) 将模型与 JComboBox 链接。链接后,会触发事件。

    【讨论】:

    • 您好,欢迎来到 StackOverflow!这可能是一个很好且解释清楚的答案,但如果更多人能够理解,那就太好了,所以请翻译成英文。
    • 在 Stack Overflow 上发帖时,必须用英文发帖。
    【解决方案2】:

    试试这个:

           indicatorComboBox = new JComboBox() {
    
            /**
             * Do not fire if set by program.
             */
            protected void fireActionEvent() {
                // if the mouse made the selection -> the comboBox has focus
                if(this.hasFocus())
                    super.fireActionEvent();
            }
        };
    

    【讨论】:

    • 简洁优雅——谢谢!
    【解决方案3】:

    要确定是否在 actionListener 接口方法(actionPerformed() 代码块)中执行各种方法,请在源组件(combo1 或 combo2)上使用 setActionCommand()。

    对于您的示例,在向 combo2 添加元素之前,调用 setActionCommand("doNothing") 并保护您的 comboBoxActionPerformed() 方法。

    这是一个可编译的示例,它使用此原理让一个组合设置另一个组合的选定索引,同时还在 JTextField 中显示一个字符串。通过使用 setActionCommand() 并保护 comboActionPerformed() 代码块,JTextField 将循环遍历 wordBank 中的每个单词。如果 comboActionPerformed() 方法没有被保护或者 actionCommand String 没有被改变,将会触发 2 个 actionEvents 并且 textField 会跳过单词。

    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.BoxLayout;
    import javax.swing.JComboBox;
    import javax.swing.JComponent;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    
    /** @author PianoKiddo */
    public class CoolCombos extends JPanel {
        JComboBox<String> candyCombo;
        JComboBox<String> flavorCombo;
        JTextField field;
        String[] wordBank;
        int i = 0;
    
    CoolCombos() {
        super();
        initComponents();
        addComponentsToPanel();
    }
    
    private void initComponents() {
        initCombos();
        initTextField();
    }
    
    private void initCombos() {
        ActionListener comboListener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                comboActionPerformed(e);
            }
        }; 
        String[] candyList = {"Sourpatch", "Skittles"};
        String[] flavorList = {"Watermelon", "Original"};
        candyCombo = new JComboBox<>(candyList);
        candyCombo.addActionListener(comboListener);
        flavorCombo = new JComboBox<>(flavorList);
        flavorCombo.addActionListener(comboListener);
    }
    
    private void initTextField() {
        wordBank = new String[]{"Which", "Do", "You", "Like", "Better?"};
        field = new JTextField("xxxxx");
        field.setEditable(false);
        field.setText(wordBank[i]);
    }
    
    private void addComponentsToPanel() {
        this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        this.add(candyCombo);
        this.add(flavorCombo);
        this.add(field);
    }
    
    public void comboActionPerformed(ActionEvent e) {
        String command = e.getActionCommand();
        if (!command.equals("doNothing")) {
            JComboBox combo = (JComboBox) e.getSource();
            if (combo.equals(candyCombo)) {
                setOtherComboIndex(candyCombo, flavorCombo); }
            else {
                setOtherComboIndex(flavorCombo, candyCombo); }
            displayText(); //replace here for toDo() code
        }
    }
    
    private void setOtherComboIndex(JComboBox combo, JComboBox otherCombo) {
        String command = otherCombo.getActionCommand();
        otherCombo.setActionCommand("doNothing"); //comment this line to skip words.
        otherCombo.setSelectedIndex(combo.getSelectedIndex());
        otherCombo.setActionCommand(command);
    }
    
    private void displayText() {
        i++; 
        String word;
        if (i > 4) { i = 0; }
        word = wordBank[i]; 
        field.setText(word);
        this.repaint();
    }
    
    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event-dispatching thread.
     */
    private static void createAndShowGUI() {
        //Create and set up the window.
        JFrame frame = new JFrame("CoolCombos");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
        //Create and set up the content pane.
        JComponent newContentPane = new CoolCombos();
        newContentPane.setOpaque(true); //content panes must be opaque
        frame.setContentPane(newContentPane);
    
        //Display the window.
        frame.pack();
        frame.setMinimumSize(frame.getSize());
        frame.setVisible(true);
    }
    
    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
    
    }
    

    【讨论】:

      【解决方案4】:

      由于我是编程新手,所以我对我的程序的这个问题采取了愚蠢的简单路线。

      我将动作侦听器更改为具有计数器 if 语句:

      if(stopActionlistenersFromFiringOnLoad != 0){//action performed ;}

      然后在java程序创建的最后,我给计数器加了1:

      topActionlistenersFromFiringOnLoad += 1;

      【讨论】:

        【解决方案5】:

        更简洁的方法是像这样使用 lambda 表达式:

        do(comboBox, () -> comboBox.setSelectedItem("Item Name"));
        

        要使上述方法起作用,您需要在某处定义以下方法:

        public static void do(final JComboBox<String> component, final Runnable f) {
            final ActionListener[] actionListeners = component.getActionListeners();
            for (final ActionListener listener : actionListeners)
                component.removeActionListener(listener);
            try {
                f.run();
            } finally {
                for (final ActionListener listener : actionListeners)
                    component.addActionListener(listener);
            }
        }
        

        【讨论】:

          【解决方案6】:

          这行得通:

          /** Implements a Combo Box with special setters to set selected item or
            * index without firing action listener. */
          public class MyComboBox extends JComboBox {
          
          /** Constructs a ComboBox for the given array of items. */
          public MyComboBox(String[] items) {
            super(items);
          }
          
          /** Flag indicating that item was set by program. */
          private boolean isSetByProgram;
          
          /** Do not fire if set by program. */
          protected void fireActionEvent() {
            if (isSetByProgram)
              return;
            super.fireActionEvent();
          }
          
          /** Sets selected Object item without firing Action Event. */
          public void setSelection(Object item) {
            isSetByProgram = true;
            setSelectedItem(item);
            isSetByProgram = false;
          }
          
          /** Sets selected index without firing Action Event. */
          public void setSelection(int index) {
            isSetByProgram = true;
            setSelectedIndex(index);
            isSetByProgram = false;
          }
          
          }
          

          注意:您不能只覆盖 setSelectedItem(...)setSelectedIndex(...),因为当您不想禁止触发侦听器时,当用户键盘或鼠标操作实际选择项目时,它们也会在内部使用。

          【讨论】:

            【解决方案7】:

            我所做的不是添加和删除动作侦听器,而是在我的动作侦听器中有一个布尔变量,如果它必须允许该动作通过,则该变量为真,如果必须阻止该动作,则该变量为假。

            然后,当我进行一些将触发动作侦听器的更改时,我将其设置为 false

            JComboBox test = new JComboBox();
            test.addActionListener(new ActionListener()
            {
              @Override
              public void actionPerformed(ActionEvent e)
              {
                if(testActionListenerActive)
                {
                  //runn your stuff here
                }
              }
            });
            
            //then when i want to update something where i want to ignore all action evetns:
            testActionListenerActive = false;
            //do stuff here like add 
            
            SwingUtilities.invokeLater(() -> testActionListenerActive = false);
            //and now it is back enabled again
            //The reason behind the invoke later is so that if any event was popped onto the awt queue 
            //it will not be processed and only events that where inserted after the enable 
            //event will get processed.
            

            【讨论】:

            • 我喜欢这种技术,但有一个问题。在设置testActionListenerActive = false 之后,我会“做一些事情”来触发ActionListener。我假设这意味着对 ActionListener 的调用将被安排在 Swing 线程上运行?然后,我设置testActionListenerActive = true。所以,问题是——当ActionListener(我什么都不想做)实际运行时,我怎么知道testActionListenerActive 仍然是错误的?谢谢。
            • @GregValvo 我的回答希望您不要在可能触发侦听器的 awt 线程上弹出任何内容。为了解决这个问题,您可以稍后调用 SwingUtilities.invokeLater(() -> testActionListenerActive = false); 将 testActionListenerActive 设置为 true;这样做,任何可能在 awt 线程上附加事件的东西都不会被调用。只有在您的 = true 事件之后添加的事件才会得到处理。我会更新我的答案,因为这是一个有效的问题。
            • 谢谢,我在想一些类似的事情。
            • 所以,我错误地假设在我的代码对JComboBox 进行“处理”之后将对ActionListener 的调用放在Swing 事件队列的末尾。我设置了一些断点并注意到ActionListener 在“做事”之后立即触发,远在将testActionListenerActive 设置为true 之前。因此,您的原始代码将适用于我的应用程序,因为我的“做事”代码也在 Swing 线程上运行。但是,我更喜欢新版本。
            【解决方案8】:

            虽然已经晚了,但更好的选择是在修改组合框之前禁用要修改的组合框。通过这样做,您可以防止修改后的组合框触发事件,例如,当您使用 removeAllItems()addItem()

            String orderByOptions[] = {"smallest","highest","longest"};
            
            JComboBox<String> jcomboBox_orderByOption1 = new JComboBox<String(orderByOptions);
            JComboBox<String> jcomboBox_orderByOption2 = new JComboBox<String(orderByOptions);
            JComboBox<String> jcomboBox_orderByOption3 = new JComboBox<String(orderByOptions);
            
            jcomboBox_orderByOption1.addItemListener(new ItemListener()
            {
                public void itemStateChanged(ItemEvent itemEvent)
                {
                        int eventID = itemEvent.getStateChange();
            
                        if (eventID == ItemEvent.SELECTED)
                        {
                            Object selectedItem = jcomboBox_orderByOption1.getSelectedItem();
            
                            jcomboBox_orderByOption2.setEnabled(false);
                            jcomboBox_orderByOption2.removeAllItems();
            
                            for (String item: string_orderByOptions)
                            {
                                if (!item.equals(selectedItem))
                                {
                                    jcomboBox_orderByOption2.addItem(item);
                                }
                            }
            
                            jcomboBox_orderByOption2.setEnabled(true);
                        }
                    }
                });
            
            
            
                jcomboBox_orderByOption2.addItemListener(new ItemListener()
                {
                    public void itemStateChanged(ItemEvent itemEvent)
                    {
                        int eventID = itemEvent.getStateChange();
            
                        if (eventID == ItemEvent.SELECTED)
                        {
                            Object selectedItem1 = jcomboBox_orderByOption1.getSelectedItem();
                            Object selectedItem2 = jcomboBox_orderByOption2.getSelectedItem();
            
                            jcomboBox_orderByOption3.setEnabled(false);
            
                            jcomboBox_orderByOption3.removeAllItems();
            
                            for (String item: string_orderByOptions)
                            {
                                if (!item.equals(selectedItem1) && !item.equals(selectedItem2))
                                {
                                    jcomboBox_orderByOption3.addItem(item);
                                }
                            }
            
                            jcomboBox_orderByOption3.setEnabled(true);
            
                        }
                    }
                });
            

            【讨论】:

              【解决方案9】:

              您可以在添加新元素之前删除动作侦听器,并在完成后将其添加回来。 Swing 是单线程的,因此无需担心其他线程需要触发侦听器。

              您的听众可能还可以检查是否选择了某些内容,如果没有,则采取适当的措施。比获得 NPE 更好。

              【讨论】:

              • 我尝试了同样的方法,但它仍然生成分配给组合框的操作,每当我使用 removeAllItems() 方法删除项目时
              • 感谢“对象”,删除动作监听器,更新组合框,然后添加动作监听器,对我有用。
              • 也为我工作。我用来自数据库的数据填充组合框。选择第一项时运行数据库查询(“选择我以填充列表”)。如果无法连接,盒子的背景会变黄,并显示警告消息而不是“选择我”。 3 秒后,计时器事件删除警告消息并恢复为“选择我”。在此解决方案之前,每次它会恢复时,它都会重新尝试查询,因为恢复消息算作actionPerformed()
              猜你喜欢
              • 1970-01-01
              • 2015-06-23
              • 2011-03-09
              • 1970-01-01
              • 1970-01-01
              • 2011-11-15
              • 1970-01-01
              相关资源
              最近更新 更多