Basically there is a JPanel on which I want to know when the mouse enters the area of the JPanel and exits the area of the JPanel. So I added a mouse listener, but if there
Here is one way to do it for a component that may contain other components:
Add a global AWT event listener to get all mouse events. For example:
Toolkit.getDefaultToolkit().addAWTEventListener(
new TargetedMouseHandler( panel ), AWTEvent.MOUSE_EVENT_MASK );
Implement the TargetedMouseHandler
to ignore events that aren't sourced by the panel or by one of the panel's children (you can use SwingUtilities.isDescendingFrom
to test for this).
Keep track of whether or not the mouse is already within the bounds of your panel. When you get a MouseEvent.MOUSE_ENTERED
event in your panel or one of its children, set a flag to true.
When you get a MouseEvent.MOUSE_EXITED
event, only reset the flag if the point in the MouseEvent
is outside the bounds of your target panel. SwingUtilities.convertPoint
and Component.getBounds().contains()
will come in handy here.
There is a very easy solution for this problem that can work :
public class MyJPanel implements MouseListener {
public void mouseExited(MouseEvent e) {
java.awt.Point p = new java.awt.Point(e.getLocationOnScreen());
SwingUtilities.convertPointFromScreen(p, e.getComponent());
if(e.getComponent().contains(p)) {return;}
...//the rest of your code
}
...
}
This way you just ignore the mouseExited event when it occurs on a child element.
A simpeler solution with java 1.8+
public class MyJPanel implements MouseListener {
public void mouseExited(MouseEvent e) {
if(!this.contains(e.getPoint())) {
... //the rest of your code
}
}
...
}
This is sample code implementing Ash's solution. For me, the JFrame did not detect all exit events properly, but an inner JPanel did, so I passed in two components - one for testing descendants and one for testing the boundary.
Toolkit.getDefaultToolkit().addAWTEventListener(
new TargetedMouseHandler(this, this.jPanel),
AWTEvent.MOUSE_EVENT_MASK);
}
public class TargetedMouseHandler implements AWTEventListener
{
private Component parent;
private Component innerBound;
private boolean hasExited = true;
public TargetedMouseHandler(Component p, Component p2)
{
parent = p;
innerBound = p2;
}
@Override
public void eventDispatched(AWTEvent e)
{
if (e instanceof MouseEvent)
{
if (SwingUtilities.isDescendingFrom(
(Component) e.getSource(), parent))
{
MouseEvent m = (MouseEvent) e;
if (m.getID() == MouseEvent.MOUSE_ENTERED)
{
if (hasExited)
{
System.out.println("Entered");
hasExited = false;
}
} else if (m.getID() == MouseEvent.MOUSE_EXITED)
{
Point p = SwingUtilities.convertPoint(
(Component) e.getSource(),
m.getPoint(),
innerBound);
if (!innerBound.getBounds().contains(p))
{
System.out.println("Exited");
hasExited = true;
}
}
}
}
}
}
If you want to get all events sent to a top-level window you can add a listener to the glass pane of the JFrame. See getGlassPane.