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

9

6

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.

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.

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.

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.

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.

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.

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

Scampi answered 10/3, 2011 at 10:49 Comment(1)
The post by slowbear looks most simple and effective. Going with this one.Cons
F
11

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.

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

Fortney answered 10/3, 2011 at 10:56 Comment(3)
i tried the same but, its still generating the action assigned with combo box, whenever i am removing items using removeAllItems() methodNun
Thanks "objects", removing action listener, updating combo box and then adding action listener back, worked for me.Eighteenth
worked for me as well. I populate a combobox with data from a DB. The DB query is ran when the first item is selected ("Select me to populate list"). If it cannot connect, the box's background is yellowed and a warning message shows instead of "Select me". 3 seconds later, a timer event removes the warning message and revert back to "Select me". Prior to this solution, everytime it would revert back, it would re-attempt the query, since reverting the message counts as an actionPerformed()Audraaudras
S
9

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

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.
Slaver answered 26/3, 2014 at 19:59 Comment(4)
I like this technique, but have a question. After setting testActionListenerActive = false, I then "do stuff" that will trigger the ActionListener. I assumes this means that a call to the ActionListener will be scheduled to run on the Swing thread? Then, I set testActionListenerActive = true. So, the question -- how do I know that testActionListenerActive will still be false when the ActionListener (that I want to do nothing) actually runs? Thanks.Dray
@GregValvo My answer expects you do not pop anything onto the awt thread that might fire the listener. To combat this issue you could set the testActionListenerActive to true with a invoke later SwingUtilities.invokeLater(() -> testActionListenerActive = false); Doing it this way anything that might have appended an event on the the awt thread will still not be called. And only events that get added after your = true event will get processed. I will update my answer since it is a valid concern.Slaver
Thanks, I was thinking something along those lines.Dray
So, I was wrong in assuming that the calls to the ActionListener would be put at the end of the Swing event queue after my code "did stuff" to the JComboBox. I put in some breakpoints and noticed that the ActionListener fires immediately after "doing stuff", well before the statement setting testActionListenerActive to true. So, your original code would have worked for my application since my "doing stuff" code was also running on the Swing thread. But, I prefer the newer version.Dray
F
7

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();
        }
    };
Frida answered 27/3, 2018 at 20:28 Comment(2)
Simple and elegant - thank you!Hawthorne
This looks to be working very well ATM. Minor style comment, I do prefer seeing spaces between the "if" keyword and the opening bracket.Cons
A
3

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

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

            }
        }
    });
Abortion answered 13/9, 2014 at 8:12 Comment(0)
L
2

The cleaner way is to use lambda expressions like this:

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);
    }
}
Lorindalorine answered 3/5, 2016 at 0:37 Comment(0)
L
1

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.

Lyda answered 5/4, 2016 at 11:47 Comment(0)
C
0

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).

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

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.

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

}
Creamery answered 1/6, 2017 at 7:9 Comment(0)
L
-1

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(stopActionlistenersFromFiringOnLoad != 0){//action performed ;}

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

topActionlistenersFromFiringOnLoad += 1;

Locomotor answered 4/9, 2015 at 7:33 Comment(0)
M
-1

To avoid that addItem method fire events is better to use an DefaultComboBoxModel in the JComboBox to add data. Also, if you invoke a model.addElement(), an event is fired, so, you can add all the elements to the model and later use JComboBox.setModel(model). In this way, if you add elements to the model, events are not fired because you have not link the JComboBox with the model. Then, I show you an example.

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

First, we create the model, add all elements to the model (events are not fired because you have not link the JComboBox with the model), we link the model with the JComboBox using ArrendatarioComboBox.setModel(model). After linking, events are fired.

Morgenthaler answered 23/7, 2020 at 14:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.