JList - deselect when clicking an already selected item

瘦欲@ 提交于 2020-01-27 23:49:29

问题


If a selected index on a JList is clicked, I want it to de-select. In other words, clicking on the indices actually toggles their selection. Didn't look like this was supported, so I tried

list.addMouseListener(new MouseAdapter()
{
   public void mousePressed(MouseEvent evt)
   {
      java.awt.Point point = evt.getPoint();
      int index = list.locationToIndex(point);
      if (list.isSelectedIndex(index))
         list.removeSelectionInterval(index, index);
   }
});

The problem here is that this is being invoked after JList has already acted on the mouse event, so it deselects everything. So then I tried removing all of JList's MouseListeners, adding my own, and then adding all of the default listeners back. That didn't work, since JList would reselect the index after I had deselected it. Anyway, what I eventually came up with is

MouseListener[] mls = list.getMouseListeners();
for (MouseListener ml : mls)
   list.removeMouseListener(ml);
list.addMouseListener(new MouseAdapter()
{
   public void mousePressed(MouseEvent evt)
   {
      java.awt.Point point = evt.getPoint();
      final int index = list.locationToIndex(point);
      if (list.isSelectedIndex(index))
         SwingUtilities.invokeLater(new Runnable()
         {
            public void run()
            {
               list.removeSelectionInterval(index, index);
            }
         });
   }
});
for (MouseListener ml : mls)
   list.addMouseListener(ml);

... and that works. But I don't like it. Is there a better way?


回答1:


Looking at the Example "ListSelectionModel: Enabling Toggle Selection Mode" here: http://java.sun.com/products/jfc/tsc/tech_topics/jlist_1/jlist.html

I have modified it slightly for multi-select list boxes (changed setSelectionInterval to addSelectionInterval) and eliminated a problem with re-selection if you click to de-select and move your mouse while the mouse is down (moved the gestureStarted check for both add and remove).

objList.setSelectionModel(new DefaultListSelectionModel() {
    private static final long serialVersionUID = 1L;

    boolean gestureStarted = false;

    @Override
    public void setSelectionInterval(int index0, int index1) {
        if(!gestureStarted){
            if (isSelectedIndex(index0)) {
                super.removeSelectionInterval(index0, index1);
            } else {
                super.addSelectionInterval(index0, index1);
            }
        }
        gestureStarted = true;
    }

    @Override
    public void setValueIsAdjusting(boolean isAdjusting) {
        if (isAdjusting == false) {
            gestureStarted = false;
        }
    }

});



回答2:


How about this?

import javax.swing.DefaultListSelectionModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.ListSelectionModel;

public class A {
    public static void main(String[] args) {
        JFrame f = new JFrame("Test");
        final JList list = new JList(new String[] {"one","two","three","four"});
        list.setSelectionModel(new DefaultListSelectionModel(){


            @Override
            public void setSelectionInterval(int index0, int index1) {
                if (index0==index1) {
                    if (isSelectedIndex(index0)) {
                        removeSelectionInterval(index0, index0);
                        return;
                    }
                }
                super.setSelectionInterval(index0, index1);
            }

            @Override
            public void addSelectionInterval(int index0, int index1) {
                if (index0==index1) {
                    if (isSelectedIndex(index0)) {
                        removeSelectionInterval(index0, index0);
                        return;
                    }
                super.addSelectionInterval(index0, index1);
                }
            }

        });
        f.getContentPane().add(list);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.pack();
        f.setVisible(true);
    }

}

It works but note one side effect... If you set the mode to multi selction like this for instance:

list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION );

you cannot select multiple objects via mouse drag. Ctrl (or shift) click works. I'm sure it can be fixed but i assume you asked this for single selection lists... If not modify your question and we can start thinking for solutions to the multiple selection problem.




回答3:


I know that this question already has an accepted answer, but I thought that I'd expand a bit, since I ended up stuck on this task for a few hours.

I was trying to implement a click-to-deselect action for selected items, but my list implementation requires the use of Single-Selection mode, specified by

JList jlist = new JList(new DefaultListModel());
jlist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

Unfortunately, this led to exceptions and redundant calls for many of the solutions for the click-to-deselect problem on many SO questions, including this answer by FuryComptuers above. Due to code in DefaultListSelectionModel.class, specifically in the addSelectionInterval(int index0, int index1) and removeSelectionInterval(int index0, int index1) methods, which calls back to the setSelectionInterval(int index0, int index1) method, caused a circular call that leads to (obviously) exceptions. This "problem" code can be seen below.

 // If we only allow a single selection, channel through
    // setSelectionInterval() to enforce the rule.
    if (getSelectionMode() == SINGLE_SELECTION) {
        setSelectionInterval(index0, index1);
        return;
    }

Sawas Dalkitsis' answer solved this problem, but would still act weird when dragging the mouse on a selected item (the selected item will select and de-select itself over and over while dragging the mouse). This wouldn't seem like a problem, but (apparently) I have a shaky hand, and minor mouse movements while clicked resulted in unwanted behavior. I combined Sawas Dalkitsis answer and FuryComptuers's answer to get the following code, which seems to work as desired:

    JList jlist = new JList(new DefaultListModel());
    jList.setSelectionModel(new DefaultListSelectionModel() {
        private static final long serialVersionUID = 1L;

        boolean gestureStarted = false;

        @Override
        public void setSelectionInterval(int index0, int index1) {
            if(!gestureStarted){
            if (index0==index1) {
                if (isSelectedIndex(index0)) {
                    removeSelectionInterval(index0, index0);
                    return;
                }
            }
            super.setSelectionInterval(index0, index1);
            }
            gestureStarted = true;
        }

        @Override
        public void addSelectionInterval(int index0, int index1) {
            if (index0==index1) {
                if (isSelectedIndex(index0)) {
                    removeSelectionInterval(index0, index0);
                    return;
                }
            super.addSelectionInterval(index0, index1);
            }
        }

        @Override
        public void setValueIsAdjusting(boolean isAdjusting) {
            if (isAdjusting == false) {
                gestureStarted = false;
            }
        }

    });
    jList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

Note: I didn't check this against the ListSelectionModel.SINGLE_INTERVAL_SELECTION, as Sawas Dalkitsis did, so use caution if implementing it in that case.




回答4:


You could always the ListSelectionListener instead of deciphering the point clicked and then translating it to the item selected.

http://java.sun.com/docs/books/tutorial/uiswing/examples/events/index.html#ListSelectionDemo

http://java.sun.com/docs/books/tutorial/uiswing/events/listselectionlistener.html

http://java.sun.com/docs/books/tutorial/uiswing/examples/events/ListSelectionDemoProject/src/events/ListSelectionDemo.java

In the above link for java file there is an implementation which can be easily improved to do "deselection" :)




回答5:


I extended FuryComptuers answer to support multiple selection, and fixed an issue where setSelectionInterval didn't work if it was called directly.

public class ToggleableListSelectionModel extends DefaultListSelectionModel {
    private static final long serialVersionUID = 1L;

    private boolean mGestureStarted;

    @Override
    public void setSelectionInterval(int index0, int index1) {
        // Toggle only one element while the user is dragging the mouse
        if (!mGestureStarted) {
            if (isSelectedIndex(index0)) {
                super.removeSelectionInterval(index0, index1);
            } else {
                if (getSelectionMode() == SINGLE_SELECTION) {
                    super.setSelectionInterval(index0, index1);
                } else {
                    super.addSelectionInterval(index0, index1);
                }
            }
        }

        // Disable toggling till the adjusting is over, or keep it
        // enabled in case setSelectionInterval was called directly.
        mGestureStarted = getValueIsAdjusting();
    }

    @Override
    public void setValueIsAdjusting(boolean isAdjusting) {
        super.setValueIsAdjusting(isAdjusting);

        if (isAdjusting == false) {
            // Enable toggling
            mGestureStarted = false;
        }
    }   
}



回答6:


Nick Dandoulakis' answer didn't quite work for me when selecting multiple items at once using a mouse click while pressing Shift.

The following ListSelectionModel behaves as I'd expect it when selecting items using mouseclicks with Shift or Ctrl.

Also, holding down Shift + Ctrl and pressing either or selects items the way I want it to.

public static class ToggleableListSelectionModel extends DefaultListSelectionModel {
        private static final long serialVersionUID = 1L;

        @Override
        public void setSelectionInterval(int startIndex, int endIndex) {
            if (startIndex == endIndex) {
                if (multipleItemsAreCurrentlySelected()) {
                    clearSelection();
                }
                if (isSelectedIndex(startIndex)) {
                    clearSelection();
                }
                else {
                    super.setSelectionInterval(startIndex, endIndex);
                }
            }
            // User selected multiple items
            else {
                super.setSelectionInterval(startIndex, endIndex);
            }
        }

        private boolean multipleItemsCurrentlyAreSelected() {
            return getMinSelectionIndex() != getMaxSelectionIndex();
        }
    }


来源:https://stackoverflow.com/questions/2528344/jlist-deselect-when-clicking-an-already-selected-item

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!