В принципе есть JPanel, на котором я хочу знать, когда мышь входит в область JPanel и выходит из области JPanel. Поэтому я добавил слушателя мыши, но если на JPanel есть компоненты, и мышь проходит по одному из них, то он обнаруживается как выход на JPanel, хотя компонент находится на JPanel. Мне было интересно, знает ли кто-нибудь какой-либо способ решить эту проблему, не делая что-то вроде добавления слушателей ко всем компонентам на JPanel?
Обнаружение событий ввода/вывода мыши в любом месте на JPanel
Ответ 1
Существует очень простое решение этой проблемы:
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
}
...
}
Таким образом вы просто игнорируете событие mouseExited, когда оно встречается в дочернем элементе.
Ответ 2
Вот один из способов сделать это для компонента, который может содержать другие компоненты:
-
Добавить глобальный прослушиватель событий AWT, чтобы получить все события мыши. Например:
Toolkit.getDefaultToolkit().addAWTEventListener( new TargetedMouseHandler( panel ), AWTEvent.MOUSE_EVENT_MASK );
-
Внедрите
TargetedMouseHandler
, чтобы игнорировать события, которые не были получены панелью или одним из дочерних элементов панели (вы можете использоватьSwingUtilities.isDescendingFrom
для проверки этого). -
Следите за тем, находится ли мышь уже в пределах вашей панели. Когда вы получаете событие
MouseEvent.MOUSE_ENTERED
в своей панели или одном из своих дочерних элементов, установите флаг в true. -
Когда вы получаете событие
MouseEvent.MOUSE_EXITED
, только reset флаг, если точка вMouseEvent
находится за пределами вашей целевой панели.SwingUtilities.convertPoint
иComponent.getBounds().contains()
пригодится здесь.
Ответ 3
Это пример кода, реализующий решение Ash. Для меня JFrame не обнаружил все события выхода правильно, но внутренний JPanel сделал, поэтому я передал два компонента: один для тестирования потомков и один для проверки границы.
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;
}
}
}
}
}
}
Ответ 4
Если вы хотите, чтобы все отправления событий отправлялись в окно верхнего уровня, вы можете добавить слушателя к стеклянному стеклу JFrame. См.: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/RootPaneContainer.html#getGlassPane%28%29