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