简体   繁体   中英

Lose selection of a JTable cell

When you click on a JTable cell, the row becomes "selected", I want it so that when I click on anything else, it becomes unselected.

I'm thinking of doing this with a mouse listener on the table but not sure how to recognize (click not on table). Any ideas?

This is what I'm trying:

 jTable.addMouseListener(new MouseAdapter(){
                @Override
                public void mouseClicked(MouseEvent e){
                    System.out.println("click");}});

But it only prints click when I click in the first column and surely not when I click on something that's not the table.

When I recognize that event I'd call this method:

public void loseCellFocus()
{
    jTable.getCellEditor().stopCellEditing();
    jTable.clearSelection();
}

Use a FocusListener attached to the JTable , this will tell you when focus moves away from the table.

See How to Write a Focus Listener for more details

For example...

    table.addFocusListener(new FocusAdapter() {
        @Override
        public void focusLost(FocusEvent e) {
            loseCellFocus();
        }           
    });

This will, of course, only work when keyboard focus is transferred to a new component capable for receiving keyboard focus

This causes my loseCellFocus() method to be called right away, as soon as I click on any cell it's called

You could use JTable#setSurrendersFocusOnKeystroke or check to see if the component that focus has been transferred to is a child of the JTable

For example...

table.addFocusListener(new FocusAdapter() {
    @Override
    public void focusLost(FocusEvent e) {
        if (e.getOppositeComponent().getParent() != table) {
            loseCellFocus();
        }
    }
});

Runnable Example...

Okay, that got messy. Not only did I have to add a FocusListener to the JTable , but I had to make sure one was added to the TableCellEditor component :P

This is a proof of concept only, I'd have specialised classes which capable for either raising events or triggering the required functionality via some common interface

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import javax.swing.DefaultCellEditor;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JViewport;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            setLayout(new BorderLayout());

            JTable table = new JTable(new DefaultTableModel(10, 10));

            JTextField editorField = new JTextField(10);
            editorField.setBorder(new EmptyBorder(1, 1, 1, 1));
            editorField.addFocusListener(new FocusAdapter() {
                @Override
                public void focusLost(FocusEvent e) {
                    TableCellEditor cellEditor = table.getCellEditor();
                    if (cellEditor != null) {
                        if (!cellEditor.stopCellEditing()) {
                            cellEditor.cancelCellEditing();
                        }
                    }

                    Component gotFocus = e.getOppositeComponent();
                    if (!gotFocus.equals(table)) {
                        table.clearSelection();
                    }
                }
            });
            DefaultCellEditor editor = new DefaultCellEditor(editorField);
            table.setDefaultEditor(Object.class, editor);

            add(new JScrollPane(table));
            table.addFocusListener(new FocusAdapter() {

                @Override
                public void focusLost(FocusEvent e) {
                    Component gotFocus = e.getOppositeComponent();
                    if (!gotFocus.getParent().equals(table)) {
                        TableCellEditor cellEditor = table.getCellEditor();
                        if (cellEditor != null) {
                            if (!cellEditor.stopCellEditing()) {
                                cellEditor.cancelCellEditing();
                            }
                        }
                        table.clearSelection();
                    }
                }

            });

            JTextField field = new JTextField(10);
            add(field, BorderLayout.SOUTH);
        }

    }

}

AWTEventListener Example...

Okay, didn't really want to go this route as it ends up in a mess of cross conditions, but. Basically this monitors ALL MouseEvent and FocusEvent s, it does some backwards summersults to test for valid conditions (as we need to make sure that not only if a JTable is part of the event, but if the editor is part of the event) and based on those results, stops cell editing and clears the selection...

import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.KeyboardFocusManager;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.FocusEvent;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            JTable table = new JTable(new DefaultTableModel(5, 5));
            setLayout(new GridBagLayout());
            setBorder(new EmptyBorder(20, 20, 20, 20));
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            add(new JScrollPane(table), gbc);
            add(new JTextField(10), gbc);

            Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
                @Override
                public void eventDispatched(AWTEvent event) {
                    if (event instanceof FocusEvent) {
                        FocusEvent focusEvent = (FocusEvent) event;
                        if (focusEvent.getID() == FocusEvent.FOCUS_LOST) {
                            Component focusTo = focusEvent.getOppositeComponent();
                            Component focusFrom = focusEvent.getComponent();

                            JTable table = getTableFrom(focusFrom);
                            if (focusTo == null || !focusTo.getParent().equals(table)) {

                                stopCellEditing(table);
                                clearSelection(table);

                            }
                        }
                    } else if (event instanceof MouseEvent) {
                        MouseEvent mouseEvent = (MouseEvent) event;
                        if (mouseEvent.getID() == MouseEvent.MOUSE_CLICKED) {
                            Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
                            JTable table = getTableFrom(focusOwner);
                            System.out.println("     table = " + table);
                            System.out.println("focusOwner = " + focusOwner);
//                          if ((table != null && mouseEvent.getComponent() != table) || (focusOwner != null && !focusOwner.getParent().equals(table))) {
                            if ((table != null && mouseEvent.getComponent() != table) && (focusOwner != null && !focusOwner.getParent().equals(table))) {
                                stopCellEditing(table);
                                clearSelection(table);
                            }
                        }
                    }
                }

                protected JTable getTableFrom(Component component) {
                    JTable table = null;
                    if (component instanceof JTable) {
                        table = (JTable) component;
                    } else if (component != null && component.getParent() instanceof JTable) {
                        table = (JTable) component.getParent();
                    }
                    return table;
                }

                protected void clearSelection(JTable table) {
                    if (table != null) {
                        table.clearSelection();
                    }
                }

                protected void stopCellEditing(JTable table) {

                    if (table != null) {
                        TableCellEditor cellEditor = table.getCellEditor();
                        if (cellEditor != null) {
                            if (!cellEditor.stopCellEditing()) {
                                cellEditor.cancelCellEditing();
                            }
                        }
                    }
                }
            }, AWTEvent.FOCUS_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);
        }

    }

}

This example basically works for ALL JTable s (once the AWTEventListener is registered), but you could configure it to monitor a single table, by changing some of the event sources and comparing them to each other :P

I did this sometime back with a JTree . The focus listener etc. did not work. Finally the solution I used is to use Toolkit.getDefaultToolkit().addAWTEventListener and capture keyboard and mouse events. Look for the JTree in the component hierarchy of the event and call a method to remove the selection if the item doesn't belong to the tree.

I can search for the code if you need.

Edit

The AWTEventListener in the accepted answer is a bit more complicated than needed.

        final AWTEventListener focusTracker = new AWTEventListener() {
            @Override public void eventDispatched(AWTEvent event) {
                if (event.getID() != MouseEvent.MOUSE_CLICKED && event.getID() != KeyEvent.KEY_PRESSED)
                    return;
                if (!isPartOfTable((Component) event.getSource())) {
                    if (table.isEditing()) {
                        TableCellEditor cellEditor = table.getCellEditor();
                        cellEditor.cancelCellEditing();
                    }
                    table.clearSelection();
                    table.dispatchEvent(new FocusEvent(table, FocusEvent.FOCUS_LOST));
                }
            }

            protected boolean isPartOfTable(Component component) {
                while (component != null && component != table)
                    component = component.getParent();
                return component == table;
            }

        };
        Toolkit.getDefaultToolkit().addAWTEventListener(focusTracker, AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);

Note: You need to remove the listener when the frame is closed. I did not like the table cell still being highlighted when clicked outside. So I am posting a focus lost event to the table.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM