java - 在java中动态添加项目时,如何避免触发JComboBox的actionlistener事件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5258596/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 10:15:52  来源:igfitidea点击:

How to avoid firing actionlistener event of JComboBox when an item is get added into it dynamically in java?

javaswingjcomboboxactionlistener

提问by Param-Ganak

I need your suggestions and guidence on following task.

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

I have a frame which has two JComboBoxes supposed they are named combo1 and combo2, a JTable and other components.

我有一个框架,其中有两个 JComboBox,假设它们被命名为组合 1 和组合 2、一个 JTable 和其他组件。

At initial stage when frame is visible with above component. The combo1 combobox is filled with some values but no value is selected at initial stage, the combo2 combobox is disabled and the table is empty.

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

I have added an actionListener on combo1 as well as combo2. There are two types of values in combo1 suppose those values are type1 and type2.

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

Condition 1: When we selects value type1 from Combo1 the actionListener method is called of combo1 which invokes a method which combo2 remains disabled and adds some rows to table related to selected value type1 from combo1.

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

Condition 2: when we selects value type2 from combo1 the actionListener method is called of combo1 which invokes a method who makes combo2 filled with some values related to type2 and gets enabled but no value is selected from combo2 and table also should remain empty until we selects any value from combo2.

条件 2:当我们从组合 1 中选择类型 2 的值时,组合 1 调用 actionListener 方法,该方法调用一个方法,该方法使组合 2 填充与类型 2 相关的一些值并被启用,但未从组合 2 中选择值,表也应保持为空,直到我们选择来自组合 2 的任何值。

table at every addition of value to combo2 the action listener method of combo2 is gets fired. In actionListener method of combo2 which gets combo2 selected value but here there is no selected value of combo2 which leads to a NullPointerException.

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

So what should I do that the action listner method of combo2 will not be get executed after addition of an values to combo2.

那么我应该怎么做才能在将值添加到组合 2 后不会执行组合 2 的动作监听器方法。

回答by objects

You could remove the action listener before you add the new elements, and add it back once you're done . Swing is single threaded so there is no need to worry about other threads needing to fire the listener.

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

Your listener could probably also check if something is selected and take appropriate action if not. Better than getting a NPE.

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

回答by Ludger

What i do instead of adding and removing action listeners i have a boolean variable in my action listeners that is true if it has to allow the action through or false if it has to block it.

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

I then set it to false when i do some changes that will fire off the action listener

然后当我做一些会触发动作监听器的更改时将它设置为 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.

回答by johnny

although its late, a better alternative would be to disabled the combobox to be modified prior to being modified. by doing so, you prevent firing events of the modified combobox, when for example, you use methods likes removeAllItems()or addItem()

虽然它晚了,但更好的选择是在修改之前禁用要修改的组合框。通过这样做,您可以防止触发修改后的组合框的事件,例如,当您使用诸如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);

            }
        }
    });

回答by Saeid Nourian

The cleaner way is to use lambda expressions like this:

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

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

For the above to work, you need the following method defined somewhere:

为了使上述工作,您需要在某处定义以下方法:

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);
    }
}

回答by slowbear

try this:

试试这个:

       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();
        }
    };

回答by Klaus Engelhardt

This works:

这有效:

/** 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;
}

}

Note: You can't just override setSelectedItem(...)or setSelectedIndex(...)because these are also used internally when items are actually selected by user keyboard or mouse actions, when you do not want to inhibit firing the listeners.

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

回答by PianoKiddo

To determine whether or not to perform various methods in actionListener interface methods (actionPerformed() blocks of code) use setActionCommand() on source components (combo1 or combo2).

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

For your example, before adding elements to combo2, call setActionCommand("doNothing") and guard your comboBoxActionPerformed() method.

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

Here's a compilable example that uses this principle to have one combo set another combo's selected index while also displaying a String in a JTextField. By using setActionCommand() and guarding the comboActionPerformed() block of code, the JTextField will cycle through each word in the wordBank. If the comboActionPerformed() method was not guarded or if the actionCommand String was not changed, 2 actionEvents will trigger and the textField will skip words.

这是一个可编译的示例,它使用此原则让一个组合设置另一个组合的选定索引,同时还在 JTextField 中显示一个字符串。通过使用 setActionCommand() 并保护 comboActionPerformed() 代码块,JTextField 将循环遍历 wordBank 中的每个单词。如果未保护 comboActionPerformed() 方法或未更改 actionCommand 字符串,则将触发 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();
        }
    });
}

}

回答by Lance Egan

I kind of went the stupid simple route with this issue for my program since I am new to programming.

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

I changed the action listeners to have a counter if statement:

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

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

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

Then at the end of the java program creation, I added 1 to the counter:

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

topActionlistenersFromFiringOnLoad += 1;

topActionlistenersFromFiringOnLoad += 1;