Search code examples
javaswingjtablemouseeventrenderer

Why doesn't my JPanel hear mouse changes when inside a JTable?


Here is a simple example of the inner JPanels of my JTable not being notified when a mouse enters / exits. Why?

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;

import net.miginfocom.layout.CC;
import net.miginfocom.swing.MigLayout;


public class Test {

    /**
     * @param args
     */
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel panel = new JPanel(new MigLayout("debug"));

        // JTABLE
        String[] columnNames = {"First Name",
                "Last Name",
                "Sport",
                "# of Years",
                "Vegetarian"};
        Object[][] data = {
                {"Kathy", "Smith",
                 "Snowboarding", new Integer(5), new Boolean(false)},
                {"John", "Doe",
                 "Rowing", new Integer(3), new Boolean(true)},
                {"Sue", "Black",
                 "Knitting", new Integer(2), new Boolean(false)},
                {"Jane", "White",
                 "Speed reading", new Integer(20), new Boolean(true)},
                {"Joe", "Brown",
                 "Pool", new Integer(10), new Boolean(false)}
            };


        JTable table = new JTable(data, columnNames);
        table.setDefaultRenderer(String.class, new TableCellRenderer() {

            @Override
            public Component getTableCellRendererComponent(JTable table, Object value,
                    boolean isSelected, boolean hasFocus, int row, int column) {
                JPanel panel = new JPanel(new MigLayout("fill"));

                JLabel label = new JLabel(value.toString());
                label.setFont(new Font("SansSerif", Font.PLAIN, 13));
                label.setHorizontalAlignment(JLabel.CENTER);
                panel.add(label, new CC().growY());

                final JLabel close = new JLabel(" x ");
                close.setHorizontalAlignment(JLabel.CENTER);
                close.setVerticalAlignment(JLabel.CENTER);
                panel.add(close, new CC().growY().alignX("right"));

                panel.addMouseListener(new MouseAdapter() {

                    @Override
                    public void mouseEntered(MouseEvent event) {
                        System.out.println("CALLED");
                        close.setForeground(Color.red);
                    }

                    @Override
                    public void mouseExited(MouseEvent event) {
                        System.out.println("GRR");
                        close.setForeground(Color.black);
                    }

                });

                return panel;
            }

        });

        panel.add(table, new CC().width("50%"));

        frame.setContentPane(panel);

        frame.pack();
        frame.setVisible(true);

    }

}

Solution

  • The renderer component is intentended to be reused for every cell so to improve performance. So make panel, label, and close fields and merely do label.setText(String,valueOf(value)).

    And then it often does not make much sense to add mouse (motion) listeners to the component. Instead add them to the JTable and use columnAtPoint, rowAtPoint.