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

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

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

});

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

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

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

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

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

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

Savvas Dalkitsis решил эту проблему, но он все равно будет вести себя странно, если перетаскивать мышь на выбранный элемент (выбранный элемент будет выделяться и отменять выбор снова и снова при перетаскивании мыши). Это не кажется проблемой, но (по-видимому) у меня дрожит рука, и незначительные движения мыши при нажатии приводят к нежелательному поведению. Я объединил ответ FuryComputers Savvas Dalkitsis ответ FuryComputers, чтобы получить следующий код, который, кажется, работает как нужно:

    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 Savvas Dalkitsis делал Savvas Dalkitsis, будьте осторожны при его реализации в этом случае.

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

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

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

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

Ответ Ника Дандулакиса мне не помог, когда я выбирал несколько элементов одновременно, щелкая мышью и нажимая 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();
        }
    }
Другие вопросы по тегам