JList - отменить выбор при нажатии на уже выбранный элемент

Если щелкнуть выбранный индекс в 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);

... и это работает. Но мне это не нравится. Есть ли способ лучше?


person Pete    schedule 27.03.2010    source источник
comment
Честно говоря, я бы сделал так, как вы уже описали, используя MouseAdapter. Если есть более элегантное решение, я тоже хотел бы услышать об этом.   -  person Joe Carnahan    schedule 27.03.2010
comment
Это поддерживается по умолчанию в JList, но вам нужно нажать Ctrl-Click (по крайней мере, в Windows) на выбранном элементе, чтобы отменить его выбор.   -  person lbalazscs    schedule 03.02.2016


Ответы (6)


Глядя на пример «ListSelectionModel: включение режима переключения выбора» здесь: http://java.sun.com/products/jfc/tsc/tech_topics/jlist_1/jlist.html

Я немного изменил его для списков с множественным выбором (изменил setSelectionInterval на addSelectionInterval) и устранил проблему с повторным выбором, если вы нажмете, чтобы отменить выбор, и переместите мышь, когда мышь нажата (переместил проверкуgestStarted для добавления и Удалить).

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

});
person FuryComputers    schedule 08.02.2012

Как насчет этого?

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) работает. Я уверен, что это можно исправить, но я предполагаю, что вы просили об этом для списков одиночного выбора... Если не изменить свой вопрос, и мы можем начать думать о решениях проблемы множественного выбора.

person Savvas Dalkitsis    schedule 28.03.2010

Я знаю, что на этот вопрос уже есть принятый ответ, но я решил немного расширить его, так как в итоге я застрял на этой задаче на несколько часов.

Я пытался реализовать действие «щелкнуть, чтобы отменить выбор» для выбранных элементов, но моя реализация списка требует использования режима одиночного выбора, указанного

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

К сожалению, это привело к исключениям и избыточным вызовам для многих решений проблемы щелчка для отмены выбора по многим вопросам SO, включая этот ответ от FuryComptuers выше. Из-за кода в 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 ответ и FuryComptuers ответ, чтобы получить следующий код, который, кажется, работает как надо:

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

Примечание. Я не проверял это на ListSelectionModel.SINGLE_INTERVAL_SELECTION, как это сделал Савас Далкитсис, поэтому будьте осторожны при реализации его в тот случай.

person shawmanz32na    schedule 10.12.2012

Вы всегда можете использовать 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-файла есть реализация, которую можно легко улучшить, чтобы сделать «отмену выбора» :)

person Kannan Ekanath    schedule 27.03.2010
comment
Если щелкнуть уже выбранный элемент, ListSelectionListener.valueChanged не будет вызываться, так как выделение не изменяется. Таким образом, вы не можете отменить выбор уже выбранного элемента внутри этого метода. - person ablaeul; 07.05.2010

Я расширил ответ 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;
        }
    }   
}
person Nick Dandoulakis    schedule 09.05.2014

Ответ Ника Дандулакиса не совсем сработал для меня при выборе нескольких элементов одновременно щелчком мыши при нажатой клавише Shift.

Следующий ListSelectionModel ведет себя так, как я ожидал, при выборе элементов с помощью щелчков мыши с Shift или Ctrl.

Кроме того, удерживая нажатой 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();
        }
    }
person Matthias Braun    schedule 10.07.2015