Search code examples
javaswingmouselistenerjcomponent

Java: Add MouseListener to custom JComponent


If I create a new class which inherits from JComponent, whose paintComponent(Graphics g) method I override, by painting a circle using g, what should I modify in order for a MouseListener to trigger only when I click inside the bounds of the component?

Because in the constructor of my component I added setBounds(...) and then added a MouseListener, but it fires every time I click anywhere inside the container in which my custom component is and not only when I click inside it.

I do not want to check in the mouseClicked() method whether the event happened inside my component or not, I want it to be called only when the click was inside.

Here's my code:

public class Node extends JComponent {
    private int x, y, radius;

    public Node(int xx, int yy, int r) {
        x = xx;
        y = yy;
        radius = r;
        this.setBounds(new Rectangle(x - r, y - r, 2 * r, 2 * r));
        setPreferredSize(new Dimension(2 * r, 2 * r));
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D gr = (Graphics2D) g;
        gr.setColor(Color.BLACK);
        gr.drawOval(x - radius, y - radius, 2 * radius, 2 * radius);
    }

    public static void main(String[] args) {
        final JFrame f = new JFrame();
        f.setSize(new Dimension(500, 500));
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final JPanel p = new JPanel();
        p.setLayout(new BorderLayout());
        Node n = new Node(100, 100, 25);
        n.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                super.mouseClicked(e);
                System.out.println("clicked");
            }
        });
        p.add(n);
        f.add(p);
        f.setVisible(true);
    }
}

Solution

  • Your mouse listener is working correctly as it is only functioning within the bounds of your JComponent. To prove it to yourself, put a Border around your component to see what it actually covers:

    public Node(int xx, int yy, int r) {
       //. ....
       setBorder(BorderFactory.createTitledBorder("Node"));
    }
    

    Understand that you're component is being added to a BorderLayout-using container in the default (BorderLayout.CENTER) position, and thus it fills the container. It does not matter that you set the bounds of your component (something that you should not be doing) or set its preferred size (also something that usually should be avoided).

    For my money, I'd make my Node a logical class, one that implements the Shape interface, not a class that extends a JComponent, and then I could use the Shape's contains(Point p) method whenever I need to know if my Node has been clicked.