JList - 单击已选择的项目时取消选择

Posted

技术标签:

【中文标题】JList - 单击已选择的项目时取消选择【英文标题】:JList - deselect when clicking an already selected item 【发布时间】:2011-02-01 11:16:02 【问题描述】:

如果单击 JList 上的选定索引,我希望它取消选择。换句话说,单击索引实际上会切换它们的选择。好像不支持,所以我尝试了

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

这里的问题是,这是在 JList 已经对鼠标事件起作用之后调用的,所以它取消选择所有内容。因此,我尝试删除所有 JList 的 MouseListeners,添加我自己的,然后将所有默认侦听器添加回来。这不起作用,因为 JList 在我取消选择索引后会重新选择它。无论如何,我最终想出的是

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

... 这行得通。但我不喜欢它。有没有更好的办法?

【问题讨论】:

老实说,我会按照您已经描述的那样使用MouseAdapter。如果有更优雅的解决方案,我也很想听听。 JList 默认支持此功能,但您必须按住 Ctrl 并单击(至少在 Windows 上)选中的项目才能取消选择它。 【参考方案1】:

您始终可以使用 ListSelectionListener 而不是破译单击的点,然后将其转换为选定的项目。

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

在上面的 java 文件链接中,有一个实现可以很容易地改进为“取消选择”:)

【讨论】:

如果在一个已经选择的元素上被点击,ListSelectionListener.valueChanged 将不会被调用,因为选择没有改变。所以你不能在这个方法中取消选择已经选择的元素。【参考方案2】:

这个怎么样?

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


它可以工作,但请注意一个副作用...例如,如果您将模式设置为多选:

list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION );

您不能通过鼠标拖动选择多个对象。 Ctrl(或 shift)单击有效。我确信它可以修复,但我假设你问过这个单选列表...如果不修改你的问题,我们可以开始考虑多选问题的解决方案。

【讨论】:

【参考方案3】:

在此处查看示例“ListSelectionModel:启用切换选择模式”: http://java.sun.com/products/jfc/tsc/tech_topics/jlist_1/jlist.html

我已经为多选列表框稍微修改了它(将 setSelectionInterval 更改为 addSelectionInterval)并消除了如果您单击以取消选择并在鼠标按下时移动鼠标时重新选择的问题(移动了gestureStarted 检查添加和删​​除)。

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;
        
    

);

【讨论】:

【参考方案4】:

我知道这个问题已经有了一个可以接受的答案,但我想我会扩大一点,因为我最终在这个任务上停留了几个小时。

我试图为选定的项目实现单击取消选择操作,但我的列表实现需要使用由指定的单选模式

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

不幸的是,这导致了许多 SO 问题的点击取消选择问题的许多解决方案的异常和冗余调用,包括上面的FuryComptuers 的this answer。由于DefaultListSelectionModel.class 中的代码,特别是addSelectionInterval(int index0, int index1)removeSelectionInterval(int index0, int index1) 方法中的代码,它们回调setSelectionInterval(int index0, int index1) 方法,导致循环调用导致(显然)异常。这个“问题”代码如下所示。

 // 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 解决了这个问题,但是在将鼠标拖动到选定项目上时仍然会表现得很奇怪(选定项目会在拖动鼠标时一遍又一遍地选择和取消选择自身)。这似乎不是问题,但是(显然)我的手颤抖,单击时鼠标的轻微移动会导致不必要的行为。我将Sawas Dalkitsis answer 和FuryComptuers 的answer 结合起来得到以下代码,这似乎可以按预期工作:

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

注意:我没有像 Sawas Dalkitsis 那样对照ListSelectionModel.SINGLE_INTERVAL_SELECTION 进行检查,因此在这种情况下实施时要小心。

【讨论】:

【参考方案5】:

我扩展了FuryComptuers答案以支持多选,并修复了直接调用setSelectionInterval不起作用的问题。

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 在按下 Shift 的同时使用鼠标单击一次选择多个项目时对我来说不太适用。

以下ListSelectionModel 在使用带有 ShiftCtrl 的鼠标单击选择项目时的行为与我预期的一样。

此外,按住 Shift + Ctrl 并按 可以按照我想要的方式选择项目。

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

【讨论】:

以上是关于JList - 单击已选择的项目时取消选择的主要内容,如果未能解决你的问题,请参考以下文章

JList 右键单击​​显示菜单(使用、删除、取消)

Java swing弹出菜单和jlist

JList 复制选择

禁用 JList 中的项目

Vaadin - ListSelect 组件在单击时取消选择

JList - 选择多个项目