How to prevent JList from making selection outside cell bounds?

不羁的心 提交于 2019-12-09 15:26:40

问题


"Is there any way to prevent a JList from selecting the last element when the user clicks below it on the list?"

That is the question someone asked here and I have the same problem. That guy found a so-so solution (by overriding processMouseEvent() ) but I want to know if there is a better/more elegant way to do it.

[Edit]

Ok, more detail. If you have a JList and there is some space unoccupied by any cell/element and you click that space then the last element in the JList got selected.

For a real example try this JList Swing Tutorial example, click the white space and see that Rollo got selected.


回答1:


See https://forums.oracle.com/forums/thread.jspa?threadID=2206996

import java.awt.EventQueue;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;

public class TestJList {
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                JList list = new JList(new Object[] { "One", "Two", "Three" }) {
                    @Override
                    public int locationToIndex(Point location) {
                        int index = super.locationToIndex(location);
                        if (index != -1 && !getCellBounds(index, index).contains(location)) {
                            return -1;
                        }
                        else {
                            return index;
                        }
                    }
                };

                list.addMouseListener(new MouseAdapter() {

                    @Override
                    public void mouseClicked(MouseEvent e) {
                        JList list = (JList) e.getSource();
                        if (list.locationToIndex(e.getPoint()) == -1 && !e.isShiftDown()
                                && !isMenuShortcutKeyDown(e)) {
                            list.clearSelection();
                        }
                    }

                    private boolean isMenuShortcutKeyDown(InputEvent event) {
                        return (event.getModifiers() & Toolkit.getDefaultToolkit()
                                .getMenuShortcutKeyMask()) != 0;
                    }
                });

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                frame.getContentPane().add(new JScrollPane(list));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}



回答2:


Looking at the call stack, you can't really do what you want without messing with AWT-Events:

Thread [AWT-EventQueue-0] (Suspended (breakpoint at line 384 in DefaultListSelectionModel)) 
    DefaultListSelectionModel.changeSelection(int, int, int, int, boolean) line: 384    
    DefaultListSelectionModel.changeSelection(int, int, int, int) line: 415 
    DefaultListSelectionModel.setSelectionInterval(int, int) line: 459  
    TestJList$1(JList<E>).setSelectionInterval(int, int) line: 2067 
    BasicListUI$Handler.adjustSelection(MouseEvent) line: 2739  
    BasicListUI$Handler.mousePressed(MouseEvent) line: 2695 
    AWTEventMulticaster.mousePressed(MouseEvent) line: 280  
    TestJList$1(Component).processMouseEvent(MouseEvent) line: 6502 
    TestJList$1(JComponent).processMouseEvent(MouseEvent) line: 3321    
    TestJList$1.processMouseEvent(MouseEvent) line: 24  
    TestJList$1(Component).processEvent(AWTEvent) line: 6270    
    TestJList$1(Container).processEvent(AWTEvent) line: 2229    
    TestJList$1(Component).dispatchEventImpl(AWTEvent) line: 4861   
    TestJList$1(Container).dispatchEventImpl(AWTEvent) line: 2287   
    TestJList$1(Component).dispatchEvent(AWTEvent) line: 4687   
    LightweightDispatcher.retargetMouseEvent(Component, int, MouseEvent) line: 4832 
    LightweightDispatcher.processMouseEvent(MouseEvent) line: 4489  
    LightweightDispatcher.dispatchEvent(AWTEvent) line: 4422    
    JFrame(Container).dispatchEventImpl(AWTEvent) line: 2273    
    JFrame(Window).dispatchEventImpl(AWTEvent) line: 2713   
    JFrame(Component).dispatchEvent(AWTEvent) line: 4687    
    EventQueue.dispatchEventImpl(AWTEvent, Object) line: 707    
    EventQueue.access$000(EventQueue, AWTEvent, Object) line: 101   
    EventQueue$3.run() line: 666    
    EventQueue$3.run() line: 664    
    AccessController.doPrivileged(PrivilegedAction<T>, AccessControlContext) line: not available [native method]    
    ProtectionDomain$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext, AccessControlContext) line: 76    
    ProtectionDomain$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext) line: 87  
    EventQueue$4.run() line: 680    
    EventQueue$4.run() line: 678    

You could implement your own ListUI and then do whatever you want (including preventing this unwanted behaviour), but I would really not recommend to go down that path.

For what it's worth (it is done by overriding processXXXEvent methods), here is a small snippet that I actually find not that ugly and that prevents selecting objects when clicking outside their bounds:

import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

public class TestJList {

    private JList list;

    protected void initUI() {
        JFrame frame = new JFrame("test");
        list = new JList(new Object[] { "Hello", "World", "!" }) {

            private boolean processEvent(MouseEvent e) {
                int index = list.locationToIndex(e.getPoint());
                return index > -1 && list.getCellBounds(index, index).contains(e.getPoint());
            }

            @Override
            protected void processMouseEvent(MouseEvent e) {
                if (processEvent(e)) {
                    super.processMouseEvent(e);
                }
            }

            @Override
            protected void processMouseMotionEvent(MouseEvent e) {
                if (processEvent(e)) {
                    super.processMouseMotionEvent(e);
                }
            }
        };
        list.setVisibleRowCount(10);

        frame.add(new JScrollPane(list));
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new TestJList().initUI();
            }
        });
    }
}


来源:https://stackoverflow.com/questions/11138665/how-to-prevent-jlist-from-making-selection-outside-cell-bounds

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