簡體   English   中英

鼠標退出內部組件 hover

[英]Mouse exited on inner component hover

我用包含不同組件的 JPanel 創建了一個 JFrame,例如,當鼠標位於 JPanel 的邊界內時,我希望 JPanel 具有可見邊框和可見圖像。 我的問題是,一旦鼠標懸停在 JPanel 內的“可交互”組件上,它就會在鼠標退出 JPanel 時注冊。 只要它在 JPanel 的邊界內,我就希望它繪制這些東西,並且當鼠標退出 JPanel 的邊界時,邊界和圖像“消失”。 有什么辦法可以做到這一點?

這是一個小演示:

public class Test {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        new TestFrame();
    }

    static class TestFrame extends JFrame{
        JPanel panel;
        JButton hoverButton;
        JButton appearingButton;
        public TestFrame() {
            super();
            this.setDefaultCloseOperation(EXIT_ON_CLOSE);
            panel = new JPanel();
            panel.setBackground(Color.red);
            hoverButton = new JButton("Hover me!");
            appearingButton = new JButton("I appeared!");
            appearingButton.setVisible(false);
            panel.add(hoverButton);
            panel.add(appearingButton);
            panel.addMouseListener(new java.awt.event.MouseAdapter() {
                public void mouseEntered(java.awt.event.MouseEvent evt) {
                    System.out.println("Entered!");
                    appearingButton.setVisible(true);
                }
                public void mouseExited(java.awt.event.MouseEvent evt) {
                    System.out.println("Exited!");
                    appearingButton.setVisible(false);
                }
            });
            add(panel);
            setSize(new Dimension(200, 200));
            setVisible(true);
        }

    }

}

將鼠標放在 JPanel 中(覆蓋整個 JFrame)時,將出現第二個按鈕。 然而,將鼠標懸停在第一個按鈕上會使第二個按鈕消失。 只要您在 JPanel 的邊界內,我就希望顯示第二個按鈕。

這實際上聽起來要困難得多。 您需要能夠監視容器的子組件的所有鼠標事件。 不幸的是,您要么獲得了要么全部解決要么沒有解決方案。 也就是說,您要么遇到了當前的問題,即一旦另一個組件開始捕獲它們, MouseListener停止報告鼠標事件(這是鼠標偵聽器API的工作方式),或者您可以看到系統正在處理的所有鼠標事件。

這使您無需提供某種篩選過程,因此可以篩選出您不感興趣的事件,例如...

    Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
        @Override
        public void eventDispatched(AWTEvent event) {
            Object source = event.getSource();
            if (source instanceof JComponent) {
                JComponent comp = (JComponent) source;
                if (SwingUtilities.isDescendingFrom(parent, comp)) {
                    // The mouse is in the house...
                }
            }
        }
    }, AWTEvent.MOUSE_MOTION_EVENT_MASK);

(父母是您的主要容器)

這基本上將AWTEventListener附加到主事件處理框架中,該框架將告訴您所有已處理的特定類型的事件。 然后,在采取適當的措施之前,您需要檢查相關事件是否確實發生在您感興趣的環境中(您自己或其中一個孩子)。

Java 10(〜8 +?)/ 2018

自從我寫了原始答案以來,事件機制的工作方式似乎已經發生了一些變化(並且我也犯了一些小錯誤🙄😓)

為了使AWTListener生成事件,所有“感興趣的”組件都需要注冊鼠標事件

我做了一個非常基本的測試,創建了一個普通的舊JPanel (和一個按鈕),並將它們添加到父容器中並使用了...

panel.addMouseListener(new MouseAdapter() {});
panel.addMouseMotionListener(new MouseAdapter() {});
add(panel);
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
    @Override
    public void eventDispatched(AWTEvent event) {
        Object source = event.getSource();
        if (source instanceof JComponent) {
            JComponent comp = (JComponent) source;
            System.out.println(comp);
            if (SwingUtilities.isDescendingFrom(comp, TestPane.this)) {
                // The mouse is in the house...
                System.out.println("Mouse in the house");
            }
        }
    }
}, AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);

這會同時為按鈕和面板生成事件

一種解決方案是向JPanel中的每個子組件添加一個鼠標偵聽器。

使用您的代碼,這是一種方法:

package com.ggl.testing;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class HoverTest {

    /**
     * @param args
     *            the command line arguments
     */
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                HoverTest hoverTest = new HoverTest();
                hoverTest.new TestFrame();
            }
        });
    }

    public class TestFrame extends JFrame {
        private static final long serialVersionUID = 6304847277329579360L;

        JPanel panel;

        JButton hoverButton;
        JButton appearingButton;

        public TestFrame() {
            super();
            this.setDefaultCloseOperation(EXIT_ON_CLOSE);
            panel = new JPanel();
            panel.setBackground(Color.red);

            hoverButton = new JButton("Hover me!");

            appearingButton = new JButton("I appeared!");
            appearingButton.setVisible(false);

            ButtonListener listener = new ButtonListener(appearingButton);

            panel.add(hoverButton);
            panel.add(appearingButton);
            panel.addMouseListener(listener);

            hoverButton.addMouseListener(listener);
            appearingButton.addMouseListener(listener);

            add(panel);

            setSize(new Dimension(200, 200));
            setVisible(true);
        }

    }

    private class ButtonListener extends MouseAdapter {

        private JButton appearingButton;

        public ButtonListener(JButton appearingButton) {
            this.appearingButton = appearingButton;
        }

        @Override
        public void mouseEntered(java.awt.event.MouseEvent evt) {
            System.out.println("Entered!");
            appearingButton.setVisible(true);
        }

        @Override
        public void mouseExited(java.awt.event.MouseEvent evt) {
            System.out.println("Exited!");
            appearingButton.setVisible(false);
        }
    }

}

實際上,這比您想象的要容易得多:

panel.addMouseListener(new java.awt.event.MouseAdapter() {
     public void mouseEntered(java.awt.event.MouseEvent evt) {
          System.out.println("Entered!");
          appearingButton.setVisible(true);
      }
      public void mouseExited(java.awt.event.MouseEvent evt) {
           if( ! panel.contains( evt.getPoint() ) ){
                System.out.println("Exited!");
                appearingButton.setVisible(false);
           }
      }
});

if語句將過濾掉鼠標仍在面板內時發生的所有mouseExited事件。

結果是,您最終將收到多個mouseEntered事件,而在它們之間沒有mouseExited ,但是可以輕松避免由此引起的任何問題。

當編輯單元格時,我發現自己處於與JTable類似的情況。

JTable有一個鼠標偵聽器,它正在跟蹤鼠標位置,但是一旦開始編輯單元格,表格就不會收到mouseExited / MOUSE_EXITED事件。 起初我選擇檢查指針是否在表邊界內,例如

@Override
public void mouseExited(MouseEvent e) {
    var lastestLocation = MouseInfo.getPointerInfo().getLocation();
    SwingUtilities.convertPointFromScreen(location, component);

    if (table.contains(latestLocation)) return; // bail out

    // do something with the table row
}

當收到鼠標退出事件時它正在工作,但我發現有時當指針移動有點不同時,通常太快,然后鼠標退出事件沒有被觸發,即使指針實際上移出了JTable

通過嘗試MadProgrammer的解決方案,問題消失了。 這里有一個小的修改,它使用邊界而不是遍歷組件樹來執行檢查。 我還想發出與表相關的事件,而不是實際的源。

Toolkit.getDefaultToolkit().addAWTEventListener(awtEvent -> {
    var source = awtEvent.getSource();
    if (source instanceof JComponent) {
        var comp = (JComponent) source;
        // The actual received event may come from a different component than the table
        var tableMouseEvent = SwingUtilities.convertMouseEvent(
                comp,
                (MouseEvent) awtEvent,
                interactiveTable
        );
        if (interactiveTable.contains(tableMouseEvent.getPoint())) {
            // The mouse is in the house...
            listeners.forEach(l -> l.mouseMoveWithin(tableMouseEvent));
        } else {
            // Mouse is outside
            listeners.forEach(l -> l.mouseOut(tableMouseEvent));
        }
    }
}, AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);

11.0.17+8-LTS bsd-aarch64 macOs 13.1

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM