Search code examples
javaswingjcomboboxitemlistener

JComboBox getSelectedIndex not working twice?


So I have made a program that Utilizes JComboBox. I have added an item listener like so:

wellbox.addItemListener(
            new ItemListener(){
                @Override
                public void itemStateChanged(ItemEvent ie) {
                    if (ie.getStateChange() == ItemEvent.SELECTED){
                        well = (wellbox.getSelectedIndex()-1);
                        if (well >=0){selected = true;}
                    }
                }
            }
    );

wellbox being the JComboBox variable well is an int and selected is a boolean.

Now, the item listener works the first time it is executed,no problem. However, at the end of the program I have an option that prompts the user to see if they want to run the program again. The entire code is essentially wrapped in a while(true) loop that breaks if the user says no, they do not want to run again. If run again for a second time, the item listener stops working and the getSelectedIndex() is no longer returning the selected index. Does anybody know why?

when run again the JCombo Box is initialized again btw

I hope I have provided enough information to get a solution.

UPDATE:

By a suggestion give I will try to better ask my question

I have three public void methods

Upon first running the program the public "class name" method that extends the JFrame intitializes the first part1 method which builds the first JPanel and adds it to the JFrame. After a button is clicked, the first JPanel is removed from the JFrame and the first method brings us to the second method

in the second method the second JPanel is built, and added to the JFrame. After another button is click, the second JPanel is removed from the JFrame and the second method brings us to the third method

the third method builds the third JPanel and adds it to the JFrame. Then once a button is clicked everything is removed using the following code:

                    part1.removeAll();
                    part2.removeAll();
                    part3.removeAll();

The third method then removes the third JPanel from the JFrame. If the user clicks the button that says that they wish to run it again, the third method brings us to the first method again, once again rebuilding the JPanels and adding them...

In the second method, in the second JPanel I have a JComboBox being initialized and added ot the second JPanel. The first time around, it runs as it should, with the item listener returning the correct index. But once run again as mentioned before, after everything is removed and then rebuilt, the item listener no longer returns an index value. Does anyone know why?

if needed, here is pretty much my code. Any method that I have not described that I am using are uninportant. I have taken out a bunch of code not at all pertaining to this question.

main class:

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

public class ReportCardGenerator {


public static void main(String[] args) {
    debugReportGUI f = new debugReportGUI();
    f.setVisible(true);
}

}

debugReportGUI Class

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

public class debugReportGUI extends JFrame
{
JPanel part1 = new JPanel ();
JPanel part2 = new JPanel ();
JPanel part3 = new JPanel ();
JComboBox wellbox;
JButton cont = new JButton ("Continue");
int buttonW = 110, buttonL = 30, well = -1;
Actions AL = new Actions ();
String[] skills = {"", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",   "10"};

public debugReportGUI ()
{
    super ("JFrame");
    setLayout (new BorderLayout ());
    setSize (800, 700);
    setLocationRelativeTo (null);
    setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
    setResizable (false);

    add (part1, BorderLayout.CENTER);
    Part1 ();
}


public void Part1 ()
{
    part1.setLayout (null);
    part1.add (cont);
    cont.addActionListener (AL);
    cont.setBounds (325, 630, buttonW, buttonL);
    cont.setToolTipText ("Once all fields have been completed press to   continue to next part of Generator");
}


private class Actions implements ActionListener
{
    public void actionPerformed (ActionEvent ae)
    {
        if (ae.getSource () == cont)
        {
            part1.setVisible(false);
            add (part2, BorderLayout.CENTER);
            part2.setVisible (true);
            Part2 ();
        }
    }
}


public void Part2 ()
{
    part2.setLayout (null);
    wellbox = new JComboBox (skills);
    part2.add (wellbox);
    wellbox.setLocation (75, 120);
    wellbox.setSize (650, 40);
    wellbox.addItemListener (
            new ItemListener ()
            {
                //@ Override
                public void itemStateChanged (ItemEvent ie)
                {
                    if (ie.getStateChange () == ItemEvent.SELECTED)
                    {
                        well = (wellbox.getSelectedIndex () - 1);
                        System.out.println (well);
                        /*Initialized value of well is -1*/
                    }
                }
            }
    );
    JButton cont2 = new JButton ("Continue");
    cont2.setBounds (345, 625, buttonW, buttonL);
    cont2.setToolTipText ("When the skill for the ''well done'' comment and    all failed items have been selected, press button ");
    cont2.addActionListener (
            new ActionListener ()
            {
                //@ Override
                public void actionPerformed (ActionEvent ae)
                {
                    if (well >= 0)
                    {
                        part2.setVisible(false);
                        add (part3, BorderLayout.CENTER);
                        part3.setVisible (true);
                        Part3 ();
                    }
                    else{
                    JOptionPane.showMessageDialog(null,"must select an       option in the JComboBox","",JOptionPane.ERROR_MESSAGE);
                    }
                }
            }
    );
    part2.add (cont2);
}


public void Part3 ()
{
    part3.setLayout (null);
    JButton again = new JButton ("Write Another");
    again.setBounds (530, 550, buttonW + 30, buttonL);
    again.setToolTipText ("If you are finished with  report card you can                   write another one for another student by clicking this button");
    again.addActionListener (
            new ActionListener ()
            {
                //@ Override
                public void actionPerformed (ActionEvent ae)
                {
well = -1;
                    part1.removeAll ();
                    part2.removeAll ();
                    part3.removeAll ();
                    remove (part3);
                    add (part1, BorderLayout.CENTER);
                    part1.setVisible (true);
                    Part1 ();
                }
            }
    );
    part3.add(again);
}
}

Solution

  • The entire code is essentially wrapped in a while(true) loop that breaks if the user says no, they do not want to run again.

    While this is fine for simple linear console (text-only) programs, this sort of construct won't work well with event-driven programs. Instead, you should re-set your GUI's components to their original state (the details of how you do this will depend on the structure and components of your program, things we don't know yet) and get rid of that while (true) block.

    If run again for a second time, the item listener stops working and the getSelectedIndex() is no longer returning the selected index.

    My guess is that you've got incorrect references. When you re-run your code, the displayed JComboBox is not the same as the one being listened to. Why? Hard to say with the information you've posted so far but you're likely creating a new JComboBox which is somehow causing the disassociation of references.

    I hope I have provided enough information to get a solution.

    Just a general solution such as I've posted above. For a more detailed solution, you'll want to create and post your Minimal, Complete, and Verifiable example.


    Edit: a small runnable program that uses your code posted above, but one that does not reproduce the problem that you're having. Note that there are unrelated problems with your code and with my derivation of your code that I have not fixed yet:

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class ReportGUI extends JFrame {
        JPanel part1 = new JPanel();
        JPanel part2 = new JPanel();
        JPanel part3 = new JPanel();
    
        // !! added
        private JComboBox<String> wellbox;
        protected int well;
        protected boolean selected;
        private String[] DUMMY_DATA = { "Monday", "Tuesday", "Wednesday",
                "Thursday", "Friday" };
    
        public ReportGUI() {
            super("Report Card Generator");
            setLayout(new BorderLayout());
            setSize(800, 700);
            setLocationRelativeTo(null);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setResizable(false);
    
            // v.lvl = 10; add(part2, BorderLayout.CENTER); Part2();
    
            add(part1, BorderLayout.CENTER);
            Part1();
        }
    
        public void Part1() {
            JButton nextPartBtn = new JButton("Next Part");
            nextPartBtn.addActionListener(new Actions());
            part1.add(nextPartBtn);
        }
    
        private class Actions implements ActionListener {
            public void actionPerformed(ActionEvent ae) {
                remove(part1);
                add(part2, BorderLayout.CENTER);
                part2.setVisible(true);
                Part2();
    
                //!!
                revalidate();
                repaint();
            }
        }
    
        public void Part2() {
            /* building JPanel part2 */
            // !! wellbox = new JComboBox(v.wellskills[v.lvl]);
            wellbox = new JComboBox<>(DUMMY_DATA);
            part2.add(wellbox);
            wellbox.setLocation(75, 120);
            wellbox.setSize(650, 40);
            wellbox.addItemListener(new ItemListener() {
                @Override
                public void itemStateChanged(ItemEvent ie) {
                    if (ie.getStateChange() == ItemEvent.SELECTED) {
                        well = (wellbox.getSelectedIndex() - 1);
                        System.out.println(well);
                        if (well >= 0) {
                            selected = true;
                        }
                    }
                }
            });
            /* rest of building JPanel part2 */
    
            //!!
            JButton showPart3Btn = new JButton(new AbstractAction("Show Part 3") {
    
                @Override
                public void actionPerformed(ActionEvent arg0) {
                    remove(part2);
                    add(part3, BorderLayout.CENTER);
                    part3.setVisible(true);
                    Part3();
                    revalidate();
                    repaint();
                }
            });
    
            part2.add(showPart3Btn);
    
        }
    
        public void Part3() {
            /* building JPanel part3 */
            part1.removeAll();
            part2.removeAll();
            part3.removeAll();
            remove(part3);
            add(part1, BorderLayout.CENTER);
            // part1.setVisible(true);
            Part1();
            revalidate();
            repaint();
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    new ReportGUI().setVisible(true);
                }
            });
        }
    }
    

    Edit

    With your latest code, add some debugging lines, including:

    private class Actions implements ActionListener {
        public void actionPerformed(ActionEvent ae) {
            if (ae.getSource() == cont) {
                ActionListener[] listeners = ((AbstractButton) cont).getActionListeners();
                System.out.println("number of listeners added to cont: " + listeners.length);
    
                part1.setVisible(false);
                add(part2, BorderLayout.CENTER);
                part2.setVisible(true);
                Part2();
            }
        }
    }
    

    and

    public void Part2() {
        part2.setLayout(null);
        wellbox = new JComboBox(skills);
        System.out.println("wellbox created. hashcode: " + wellbox.hashCode());
        part2.add(wellbox);
        wellbox.setLocation(75, 120);
        wellbox.setSize(650, 40);
        wellbox.addItemListener(new ItemListener() {
            // @ Override
            public void itemStateChanged(ItemEvent ie) {
                if (ie.getStateChange() == ItemEvent.SELECTED) {
                    System.out.println("wellbox state change. hashcode: " + wellbox.hashCode());
                    well = (wellbox.getSelectedIndex() - 1);
                    System.out.println(well);
                }
            }
        });
    

    When you run this, you'll see that the cont button is not re-created and gets multiple ActionListeners added, so that the JComboBox displayed is not the one that is tested. I would recommend simplifying your code structure, and hang on for that...


    Edit 2
    Here is a more OOPs implementation of the MCVE, one that uses a CardLayout. It still can be improved by making it more MVC-ish, with separation of view from control from model:

    import java.awt.BorderLayout;
    import java.awt.CardLayout;
    import java.awt.Component;
    import java.awt.Dimension;
    import java.awt.event.ActionEvent;
    import java.awt.event.ItemListener;
    import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class DebugReport2 extends JPanel {
        private static final int PREF_W = 800;
        private static final int PREF_H = 700;
        public static final String PART_1 = "part 1";
        public static final String PART_2 = "part 2";
        public static final String PART_3 = "part 3";
        private CardLayout cardLayout = new CardLayout();
        private Part1Panel part1Panel = new Part1Panel(this);
        private Part2Panel part2Panel = new Part2Panel(this);
        private Part3Panel part3Panel = new Part3Panel(this);
    
        public DebugReport2() {
            setLayout(cardLayout);
            System.out.println(Part1Panel.class.getName());
            add(part1Panel, PART_1);
            add(part2Panel, PART_2);
            add(part3Panel, PART_3);
        }
    
        // public method to allow other classes to swap views
        public void showCard(String key) {
            cardLayout.show(this, key);
        }
    
        public void part2Reset() {
            part2Panel.reset();
        }
    
        public void setPart3SelectedOptionText(String selectedItem) {
            part3Panel.setSelectedOptionText(selectedItem);
        }
    
        @Override
        public Dimension getPreferredSize() {
            if (isPreferredSizeSet()) {
                return super.getPreferredSize();
            }
            return new Dimension(PREF_W, PREF_H);
        }
    
        private static void createAndShowGui() {
            DebugReport2 mainPanel = new DebugReport2();
    
            JFrame frame = new JFrame("Debug Report 2");
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            frame.getContentPane().add(mainPanel);
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    createAndShowGui();
                }
            });
        }
    }
    
    @SuppressWarnings("serial")
    class BaseContinueAction extends AbstractAction {
        private String key;
        private DebugReport2 debugReport2;
    
        public BaseContinueAction(String name, DebugReport2 debugReport2, String key) {
            super(name);
            int mnemnoic = (int) name.charAt(0);
            putValue(MNEMONIC_KEY, mnemnoic); // first letter is mnemonic alt-key press.
            this.key = key;
            this.debugReport2 = debugReport2;
        }
    
        public String getKey() {
            return key;
        }
    
        public DebugReport2 getDebugReport2() {
            return debugReport2;
        }
    
        @Override
        public void actionPerformed(ActionEvent e) {
            debugReport2.showCard(key);
        }
    }
    
    @SuppressWarnings("serial")
    class Part1Panel extends JPanel {
        private DebugReport2 debugReport2;
    
        public Part1Panel(DebugReport2 debugReport2) {
            this.debugReport2 = debugReport2;
            setBorder(BorderFactory.createTitledBorder("Part 1 Panel")); // for debug purposes
    
            JPanel bottomPanel = new JPanel();
            bottomPanel.add(new JButton(new ContinueAction("Continue", debugReport2, DebugReport2.PART_2)));
    
            setLayout(new BorderLayout());
            add(bottomPanel, BorderLayout.SOUTH);
        }
    
        private class ContinueAction extends BaseContinueAction {
    
            public ContinueAction(String name, DebugReport2 debugReport2, String key) {
                super(name, debugReport2, key);
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
                super.actionPerformed(e);
                getDebugReport2().part2Reset();
            }
    
        }
    }
    
    @SuppressWarnings("serial")
    class Part2Panel extends JPanel {
        private static final String[] DEFAULT_DATA = {"0", "1", "2", "3", "4", "5", "6"};
    
        private DebugReport2 debugReport2;
        private ComboBoxModel<String> comboModel = new DefaultComboBoxModel<>();
        private JComboBox<String> skillsCombo = new JComboBox<>(comboModel);
    
        public Part2Panel(DebugReport2 debugReport2) {
            ((DefaultComboBoxModel<String>)comboModel).addElement("");
            for (String item : DEFAULT_DATA) {
                ((DefaultComboBoxModel<String>)comboModel).addElement("Selection " + item);
            }
    
            this.debugReport2 = debugReport2;
            setBorder(BorderFactory.createTitledBorder("Part 2 Panel")); // for debug purposes
    
            JPanel centerPanel = new JPanel(); // uses default FlowLayout
            centerPanel.add(skillsCombo);
    
            JPanel bottomPanel = new JPanel(); // again default FlowLayout
            bottomPanel.add(new JButton(new ContinueAction("Continue", debugReport2, DebugReport2.PART_3)));
    
            setLayout(new BorderLayout());
            add(centerPanel, BorderLayout.CENTER);
            add(bottomPanel, BorderLayout.PAGE_END);
    
        }
    
        // if you need outside classes to be able to change the combo box model
        // also resets selected index to -1
        public void setComboModel(ComboBoxModel<String> comboModel) {
    
            // reset combobox selection to -1, but remove listeners before doing so, and then
            // re-add them afterwards
            ItemListener[] itemListeners = skillsCombo.getItemListeners();
            for (ItemListener itemListener : itemListeners) {
                skillsCombo.removeItemListener(itemListener);
            }
    
            this.comboModel = comboModel;
            skillsCombo.setModel(comboModel);
            skillsCombo.setSelectedIndex(-1);
    
            for (ItemListener itemListener : itemListeners) {
                skillsCombo.addItemListener(itemListener);
            }
        }
    
        public void reset() {
            ComboBoxModel<String> model = skillsCombo.getModel();
            setComboModel(model);
        }
    
        private class ContinueAction extends BaseContinueAction {
    
            private String selectedItem = "";
    
            public ContinueAction(String name, DebugReport2 debugReport2, String key) {
                super(name, debugReport2, key);
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
                selectedItem = (String) skillsCombo.getSelectedItem();
                if (selectedItem == null || selectedItem.trim().isEmpty()) {
                    Component parent = debugReport2;
                    String title = "Option Not Selected";
                    String message = "You must select an option before continuing";
                    int type = JOptionPane.ERROR_MESSAGE;
                    JOptionPane.showMessageDialog(parent, message, title, type);
                } else {
                    // show the next view in the card layout
                    super.actionPerformed(e);
                    getDebugReport2().setPart3SelectedOptionText(selectedItem);
                }
            }
    
        }
    }
    
    @SuppressWarnings("serial")
    class Part3Panel extends JPanel {
        private DebugReport2 debugReport2;
        private JTextField selectedOptionField = new JTextField(10);
    
        public Part3Panel(DebugReport2 debugReport2) {
            this.debugReport2 = debugReport2;
            setBorder(BorderFactory.createTitledBorder("Part 3 Panel")); // for debug purposes
            add(new JLabel("Selected Option:"));
            add(selectedOptionField);
            add(new JButton(new BaseContinueAction("Continue", debugReport2, DebugReport2.PART_1)));
        }
    
        public void setSelectedOptionText(String text) {
            selectedOptionField.setText(text);
        }
    
    }