Is there a way to prevent action listeners from firing when setSelectedItem() is called?
Asked Answered
S

3

6

I have a program with multiple comboboxes, each with their own action listener. Selecting an item from any of the comboboxes will change the items in one or more of the other comboboxes. The problem I'm running into is that calling setSelectedItem() for one combobox will fire the action listener for another combobox, which in turn fires the action listeners of the other ones, etc etc.

Is there a way to avoid this, either by only allowing the action listeners to be fired from user input, or by detecting that the action was not fired from user input? Say that not using setSelectedItem() is not an option, because I want the program to be able to set the currently selected item for each comboxbox. Thanks in advance for any help.

Sharpen answered 2/5, 2012 at 4:22 Comment(0)
P
0

for example

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;

public class ComboBoxTwo extends JFrame implements ActionListener, ItemListener {

    private static final long serialVersionUID = 1L;
    private JComboBox mainComboBox;
    private JComboBox subComboBox;
    private Hashtable<Object, Object> subItems = new Hashtable<Object, Object>();

    public ComboBoxTwo() {
        String[] items = {"Select Item", "Color", "Shape", "Fruit"};
        mainComboBox = new JComboBox(items);
        mainComboBox.addActionListener(this);
        mainComboBox.addItemListener(this);
        //prevent action events from being fired when the up/down arrow keys are used
        //mainComboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
        getContentPane().add(mainComboBox, BorderLayout.WEST);
        subComboBox = new JComboBox();//  Create sub combo box with multiple models
        subComboBox.setPrototypeDisplayValue("XXXXXXXXXX"); // JDK1.4
        subComboBox.addItemListener(this);
        getContentPane().add(subComboBox, BorderLayout.EAST);
        String[] subItems1 = {"Select Color", "Red", "Blue", "Green"};
        subItems.put(items[1], subItems1);
        String[] subItems2 = {"Select Shape", "Circle", "Square", "Triangle"};
        subItems.put(items[2], subItems2);
        String[] subItems3 = {"Select Fruit", "Apple", "Orange", "Banana"};
        subItems.put(items[3], subItems3);
//      mainComboBox.setSelectedIndex(1);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        String item = (String) mainComboBox.getSelectedItem();
        Object o = subItems.get(item);
        if (o == null) {
            subComboBox.setModel(new DefaultComboBoxModel());
        } else {
            subComboBox.setModel(new DefaultComboBoxModel((String[]) o));
        }
    }

    @Override
    public void itemStateChanged(ItemEvent e) {
        if (e.getStateChange() == ItemEvent.SELECTED) {
            if (e.getSource() == mainComboBox) {
                if (mainComboBox.getSelectedIndex() != 0) {
                    FirstDialog firstDialog = new FirstDialog(ComboBoxTwo.this,
                            mainComboBox.getSelectedItem().toString(), "Please wait,  Searching for ..... ");
                }
            } 
        }
    }

    private class FirstDialog extends JDialog {

        private static final long serialVersionUID = 1L;

        FirstDialog(final Frame parent, String winTitle, String msgString) {
            super(parent, winTitle);
            setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
            JLabel myLabel = new JLabel(msgString);
            JButton bNext = new JButton("Stop Processes");
            add(myLabel, BorderLayout.CENTER);
            add(bNext, BorderLayout.SOUTH);
            bNext.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent evt) {
                    setVisible(false);
                }
            });
            javax.swing.Timer t = new javax.swing.Timer(1000, new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    setVisible(false);
                }
            });
            t.setRepeats(false);
            t.start();
            setLocationRelativeTo(parent);
            setSize(new Dimension(400, 100));
            setVisible(true);
        }
    }

    public static void main(String[] args) {
        JFrame frame = new ComboBoxTwo();
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}
Pyrolysis answered 2/5, 2012 at 13:47 Comment(0)
P
1

I dont think it is possible. If you set actionlistener on your combobox, then actionPerformed() will always going to call whenver any event is generated on combobox. It does not check whether the event is generated by the user or through program.

But you can set mouselistner on your combobox, so only whenever you click on you combobox, then only the specified action will be taken.

Also another way is to set flag for this to check that whether the event is generated by user or through program.

But I'll preffer the first technique of setting mouselistener on combobox.

Pasahow answered 2/5, 2012 at 4:43 Comment(3)
I'm glad you mentioned this. I went with setting a flag for a case similar to this. is_editing = false; (Just to put into context)Byrne
I have a similar issue but not sure how the flag will help me. how can i change the flag back to true when the user changes it? e.g. i can easily set the flag to false when the program changes the item...but what about true?Obstruct
okay, nevermind, duh, i get it. set to false, do the combobox changing, then set back to true. and within the actionPerformed() i just check the flag.Obstruct
P
0

for example

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;

public class ComboBoxTwo extends JFrame implements ActionListener, ItemListener {

    private static final long serialVersionUID = 1L;
    private JComboBox mainComboBox;
    private JComboBox subComboBox;
    private Hashtable<Object, Object> subItems = new Hashtable<Object, Object>();

    public ComboBoxTwo() {
        String[] items = {"Select Item", "Color", "Shape", "Fruit"};
        mainComboBox = new JComboBox(items);
        mainComboBox.addActionListener(this);
        mainComboBox.addItemListener(this);
        //prevent action events from being fired when the up/down arrow keys are used
        //mainComboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
        getContentPane().add(mainComboBox, BorderLayout.WEST);
        subComboBox = new JComboBox();//  Create sub combo box with multiple models
        subComboBox.setPrototypeDisplayValue("XXXXXXXXXX"); // JDK1.4
        subComboBox.addItemListener(this);
        getContentPane().add(subComboBox, BorderLayout.EAST);
        String[] subItems1 = {"Select Color", "Red", "Blue", "Green"};
        subItems.put(items[1], subItems1);
        String[] subItems2 = {"Select Shape", "Circle", "Square", "Triangle"};
        subItems.put(items[2], subItems2);
        String[] subItems3 = {"Select Fruit", "Apple", "Orange", "Banana"};
        subItems.put(items[3], subItems3);
//      mainComboBox.setSelectedIndex(1);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        String item = (String) mainComboBox.getSelectedItem();
        Object o = subItems.get(item);
        if (o == null) {
            subComboBox.setModel(new DefaultComboBoxModel());
        } else {
            subComboBox.setModel(new DefaultComboBoxModel((String[]) o));
        }
    }

    @Override
    public void itemStateChanged(ItemEvent e) {
        if (e.getStateChange() == ItemEvent.SELECTED) {
            if (e.getSource() == mainComboBox) {
                if (mainComboBox.getSelectedIndex() != 0) {
                    FirstDialog firstDialog = new FirstDialog(ComboBoxTwo.this,
                            mainComboBox.getSelectedItem().toString(), "Please wait,  Searching for ..... ");
                }
            } 
        }
    }

    private class FirstDialog extends JDialog {

        private static final long serialVersionUID = 1L;

        FirstDialog(final Frame parent, String winTitle, String msgString) {
            super(parent, winTitle);
            setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
            JLabel myLabel = new JLabel(msgString);
            JButton bNext = new JButton("Stop Processes");
            add(myLabel, BorderLayout.CENTER);
            add(bNext, BorderLayout.SOUTH);
            bNext.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent evt) {
                    setVisible(false);
                }
            });
            javax.swing.Timer t = new javax.swing.Timer(1000, new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    setVisible(false);
                }
            });
            t.setRepeats(false);
            t.start();
            setLocationRelativeTo(parent);
            setSize(new Dimension(400, 100));
            setVisible(true);
        }
    }

    public static void main(String[] args) {
        JFrame frame = new ComboBoxTwo();
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}
Pyrolysis answered 2/5, 2012 at 13:47 Comment(0)
V
0

One option is to add a PopupMenuListener instead of an action listener. Its popupMenuWillBecomeInvisible method will fire after the user manually selects an item, but not after setSelectedItem, setSelectedIndex, or addItem is called.

I've found one limitation to this method: if the user keeps the dropdown closed and selects an item by typing its first letter, the popup listener won't fire. In the example below, I worked around this by preventing the user from selecting items by typing alphabetical characters.

import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;

public class ComboBoxExample extends JFrame {

    public static void main(String[] args) {

        JFrame frame = new JFrame("JComboBox Example");

        // Create combo box
        JComboBox homeTypeComboBox = new JComboBox(new String[]{"House", "Condo", "Apartment"});
        
        // add the popup listener
        homeTypeComboBox.addPopupMenuListener(new HomeTypeChangeListener());

        // prevent user from selecting an item by typing its first letter
        homeTypeComboBox.setKeySelectionManager((key, model) -> { return -1; });

        JPanel panel = new JPanel();
        panel.add(homeTypeComboBox);

        frame.add(panel);
        frame.setSize(300, 150);
        frame.show();

        // These will not generate print statements
        homeTypeComboBox.setSelectedItem("House");
        homeTypeComboBox.setSelectedItem("Condo");
        homeTypeComboBox.setSelectedItem("Apartment");
    }

    private static class HomeTypeChangeListener implements PopupMenuListener {
        
        public void popupMenuWillBecomeVisible(PopupMenuEvent e) {}

        public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
            // triggered when user makes a dropdown selection
            System.out.println("User changed the dropdown selection. This will not be triggered for programmatic" 
                + " calls to JComboBox::setSelectedItem, JComboBox::setSelectedIndex, or JComboBox::addItem.");
        }

        public void popupMenuCanceled(PopupMenuEvent e) {}
    }

}

Varioloid answered 20/2, 2022 at 5:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.