Подтвердить что ты не робот

Обнаружение событий ввода/вывода мыши в любом месте на JPanel

В принципе есть JPanel, на котором я хочу знать, когда мышь входит в область JPanel и выходит из области JPanel. Поэтому я добавил слушателя мыши, но если на JPanel есть компоненты, и мышь проходит по одному из них, то он обнаруживается как выход на JPanel, хотя компонент находится на JPanel. Мне было интересно, знает ли кто-нибудь какой-либо способ решить эту проблему, не делая что-то вроде добавления слушателей ко всем компонентам на JPanel?

4b9b3361

Ответ 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;
                    }
                }
            }
        }
    }
}