-1

I am not sure why everything is printing twice. For example, if you press "Male", then the program will print MaleMale. This is a part of a bigger program: I want gender (a String) to store the last selection. Is this code doing that?

DefaultListModel toAdd = new DefaultListModel();
toAdd.addElement("Male");
toAdd.addElement("Female");
toAdd.addElement("Others");

JList list = new JList();
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.setBorder(new TitledBorder(null, "How do you identify yourself?", ` 
TitledBorder.CENTER, TitledBorder.TOP, null, null));
list.setBackground(SystemColor.info);
list.setBounds(57, 85, 244, 97);
list.setModel(toAdd);
frame.getContentPane().add(list);

list.addListSelectionListener(new ListSelectionListener() {

        public void valueChanged (ListSelectionEvent event) {
            int lastSelIx = list.getMaxSelectionIndex();
            gender = (String) list.getModel().getElementAt(lastSelIx);
            System.out.print(gender);
        }       
    );
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • 1
    Are you getting two `valueChanged` events for every change? – Jim Garrison Dec 23 '17 at 23:00
  • 1
    1) For better help sooner, post a [MCVE] or [Short, Self Contained, Correct Example](http://www.sscce.org/). 2) Java GUIs have to work on different OS', screen size, screen resolution etc. using different PLAFs in different locales. As such, they are not conducive to pixel perfect layout. Instead use layout managers, or [combinations of them](http://stackoverflow.com/a/5630271/418556) along with layout padding and borders for [white space](http://stackoverflow.com/a/17874718/418556). 3) `"How do you identify yourself?"` in terms of gender.. – Andrew Thompson Dec 24 '17 at 01:26
  • 2
    .. this would more often be expressed as radio buttons (`JRadioButton`) in a `ButtonGroup`. Add an `ActonListener` to each button to get a single action event for each selection (as opposed to two events for each selection in the `JList`). – Andrew Thompson Dec 24 '17 at 01:26

2 Answers2

1

Generally speaking, the ListSelectionListener will generate two events, one for the "deselection" of the currently selected item/range and one for the "selection" of the new item/range.

If you're only interested in what has become selected, you can use ListSelectionEvent#getValueIsAdjusting, which will return true if there are more events to come and false if no more related events are coming (or the selection has become stable), for example...

import java.awt.EventQueue;
import java.awt.SystemColor;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

public class JavaApplication20 {

    public static void main(String[] args) {
        new JavaApplication20();
    }

    public JavaApplication20() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                DefaultListModel toAdd = new DefaultListModel();
                toAdd.addElement("Male");
                toAdd.addElement("Female");
                toAdd.addElement("Others");

                JList list = new JList();
                list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
                list.setBorder(new TitledBorder(null, "How do you identify yourself?", TitledBorder.CENTER, TitledBorder.TOP, null, null));
                list.setBackground(SystemColor.info);
                list.setModel(toAdd);
                frame.getContentPane().add(new JScrollPane(list));

                list.addListSelectionListener(new ListSelectionListener() {

                    public void valueChanged(ListSelectionEvent event) {
                        if (!event.getValueIsAdjusting()) {
                            int lastSelIx = list.getMaxSelectionIndex();
                            String gender = (String) list.getModel().getElementAt(lastSelIx);
                            System.out.println(gender);
                        } else {
                            // You can ignore this, I was just testing ;)
                            int lastSelIx = event.getFirstIndex();
                            String gender = (String) list.getModel().getElementAt(lastSelIx);
                            System.out.println(gender);
                        }
                    }
                });
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

}

You can have a read of How to Write a List Selection Listener for more details

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • For a moment, I also thought that the first event may be caused by a "deselection", but this is not really the case. (Maybe you (and I ;-)) mixed this up with the `JCheckBox`, where a `ChangeListener` causes a "deselection event", and usually, people want an `ActionListener` for the selection only). However, the general solution of `getValueIsAdjusting` is the same. – Marco13 Dec 24 '17 at 01:00
  • @Marco13 I'm sure I've seen it perform a double event for keyboard and programmatically selection change - think I can make it work. The first time I tested this, I couldn't get it to generate seperate events for mouse pressed and released, but the next time I ran it did - so confusion reins – MadProgrammer Dec 24 '17 at 01:28
  • 1
    This might go into details now. If one was about to investigate these details further: I also considered that it might depend on the LookAndFeel, since this may govern the behavior of such events in "complex" components like a JList. But with `getValueIsAdjusting`, one should be on the safe side. – Marco13 Dec 24 '17 at 14:33
0

There are two events generated each time when you click on an item in the list: The first one is created when you press the mouse button, and the second one when you release the button.

(This is mainly relevant for more sophisticated patterns of range-selection, drag and drop, or the case where you press the button on one item and then drag the mouse to another item)

The solution is to check whether the "selection value is still adjusting" in the ListSelectionListener. So you can just do something like

if (event.getValueIsAdjusting()) return;

in your event listener method.

Here as a MCVE, together with a few other cleanups:

import java.awt.SystemColor;

import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

public class JListSelectionDoubleEvent
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui()
    {
        DefaultListModel<String> listModel = new DefaultListModel<String>();
        listModel.addElement("Male");
        listModel.addElement("Female");
        listModel.addElement("Others");

        JList<String> list = new JList<String>(listModel);
        list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        list.setBorder(
            BorderFactory.createTitledBorder("How do you identify yourself?"));
        list.setBackground(SystemColor.info);

        list.addListSelectionListener(new ListSelectionListener()
        {
            @Override
            public void valueChanged(ListSelectionEvent event)
            {
                if (event.getValueIsAdjusting())
                {
                    //System.out.println("Adjusting. Ignore this");
                    return;
                }
                String gender = list.getSelectedValue();
                System.out.print(gender);
            }
        });

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(list);
        frame.setSize(400, 400);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}
Marco13
  • 53,703
  • 9
  • 80
  • 159