【问题标题】:How to make a glow effect under the cursor while also not affecting components underneath MouseListener (mouseEntered, mouseExit) methods?如何在光标下产生发光效果,同时不影响 MouseListener (mouseEntered, mouseExit) 方法下的组件?
【发布时间】:2018-04-25 17:39:14
【问题描述】:

背景:目前在我的项目中,我有多个组件,每个组件都有一个自定义 MouseListener。使用这些 MouseListener,大部分代码都在 mouseEntered/mouseExit 方法中。

现在我正在尝试在光标下实现发光效果。为此,我一直使用堆叠在一起的 JPanel,但对于我提供的示例代码,我将设置窗格(ContentPane 和 GlassPane)。

示例中显示的问题是 GlassPane (glowPanel) 拦截了所有 MouseEvents,因此我不知道如何合并 mouseEntered/mouseExit 事件,因为 GlassPane/glowPanel 覆盖了整个窗口:又名 mouseEntered/mouseExit 事件永远不会发生。我在想我可以随时跟踪我所在的组件,当它发生变化时,我会重新调度 mouseEntered/mouseExit,但我不确定这是否是解决此问题的最佳方法。另一种选择只是向 mouseEntered/mouseExit 发送垃圾邮件,但我知道这行不通。

在示例代码中,我提供了两种我尝试过的方法。

MouseListener方式:使用鼠标事件更新鼠标当前所在的glowPanel并重绘。无法正常工作,无法正确重新调度 mouseEntered、mouseExit,因为 GlowPanel 从不触发此类事件(

线程:让一个线程在循环中运行,每隔一段时间就休眠一次。当它唤醒时获取鼠标位置,然后用新位置重新绘制发光面板。这可行,但我认为如果我能让 mouseEntered/mouseExit 事件正常工作,MouseListener 方式总体上会更好。

我知道这段代码不是最漂亮的。我试图尽可能快地做到这一点,以展示一个例子。我希望稍后会通过代码并使其更漂亮。

public class GlowPanel extends JPanel{

//Attributes
private MouseAdapter repaintAdapter;
private MouseAdapter redispatchAdapter;
private static Color INNER_COLOR = new Color(30, 30, 30, 127);
private static Color OUTER_COLOR = new Color(90, 90, 90, 127);
private static int RADIUS = 25;
private static int DIAMETER = RADIUS * 2;
private Thread glowThread;
private static Point p = new Point(0,0);
private static GlowPanel glowPanel;
private static Boolean MOUSE_LISTEN_STATE = true;
private static Boolean GLOW_THREAD_STATE = false;
private Boolean CURRENT_STATE;
private static ButtonGroup buttonGroup;

//Start Constructor
public GlowPanel(){
    //Set Attributes
    this.setName("GLOW PANEL");
    this.setOpaque(false);
    this.setVisible(true);
    //this.event

    //Add MouseMouseListener
    this.repaintAdapter = new RepaintMouseAdapter(this);
    this.redispatchAdapter = new RedispatchAdapter();
}
//End Constructor

//Start Methods

/**
 * Set state to mouseListen
 */
public synchronized void setStateMouseListen(){
    if(!MOUSE_LISTEN_STATE.equals(CURRENT_STATE)){
        if(this.glowThread != null){
            this.glowThread.interrupt();
        }
        this.addMouseMotionListener(this.repaintAdapter);
        this.addMouseListener(this.redispatchAdapter);
        this.addMouseMotionListener(this.redispatchAdapter);
        CURRENT_STATE = MOUSE_LISTEN_STATE;
    }
}

/**
 * Set state to off (don't do any effects)
 */
public synchronized void setStateOff(){
    if(this.glowThread != null){
        this.glowThread.interrupt();
    }
    this.removeMouseMotionListener(this.repaintAdapter);
    this.removeMouseListener(this.redispatchAdapter);
    this.removeMouseMotionListener(this.redispatchAdapter);
    CURRENT_STATE = null;
}

/**
 * Set state to GlowThread
 */
public synchronized void setStateGlowThread(){
    if(!GLOW_THREAD_STATE.equals(CURRENT_STATE)){
        this.removeMouseMotionListener(this.repaintAdapter);
        this.removeMouseListener(this.redispatchAdapter);
        this.removeMouseMotionListener(this.redispatchAdapter);
        this.glowThread = new Thread(new Runnable(){
            public void run(){
                while(!Thread.currentThread().isInterrupted()){
                    try {
                        Thread.sleep(1000);
                        GlowPanel.this.p = MouseInfo.getPointerInfo().getLocation(); 
                        SwingUtilities.convertPointFromScreen(p, GlowPanel.this);
                        GlowPanel.this.repaint();
                    }   catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        });
        this.glowThread.start();
        CURRENT_STATE = GLOW_THREAD_STATE;
    }
}

@Override
public void paintComponent(Graphics g){
    super.paintComponent(g);
    Point p = GlowPanel.this.p;
    Shape circle = new Ellipse2D.Double(p.getX() - RADIUS, p.getY() - RADIUS, DIAMETER, DIAMETER);
    Graphics2D g2 = (Graphics2D) g;

    g2.setColor(GlowPanel.INNER_COLOR);

    //g2.fill(circle);
    Shape circle2 = new Ellipse2D.Double(p.getX() - RADIUS + 9, p.getY() - RADIUS + 9, DIAMETER - 18, DIAMETER - 18);
    //Area a = new Area(circle);
    //a.subtract(new Area(circle2));
    g2.fill(circle);
    g2.setColor(GlowPanel.OUTER_COLOR);
    g2.fill(circle2);
}

/**
 * Create GUI used to show example
 */
public static void createGUI(){
    JFrame jFrame = new JFrame();

    //GlowPanel
    GlowPanel.glowPanel = new GlowPanel();

    //ContentPanel
    JPanel contentPanel = new JPanel();
    GridBagLayout gBL = new GridBagLayout();
    gBL.columnWidths = new int[]{100, 100, 100};
    gBL.rowHeights = new int[]{200, 50};
    contentPanel.setLayout(gBL);

    //Initial Constraints
    GridBagConstraints gBC = new GridBagConstraints();
    gBC.fill = GridBagConstraints.BOTH;
    gBC.gridx = 0;
    gBC.gridy = 0;
    gBC.weightx = 1;
    gBC.weighty = 1;
    gBC.gridwidth = 3;

    //Hover Panel
    JPanel hoverPanel = new JPanel();
    hoverPanel.setOpaque(true);
    hoverPanel.setBackground(Color.blue);
    hoverPanel.addMouseListener(new MouseAdapter(){

        @Override
        public void mouseClicked(MouseEvent e){}

        @Override
        public void mousePressed(MouseEvent e){}

        @Override
        public void mouseReleased(MouseEvent e){}

        @Override
        public void mouseEntered(MouseEvent e){
            hoverPanel.setBackground(Color.yellow);

        }

        @Override
        public void mouseExited(MouseEvent e){
            hoverPanel.setBackground(Color.blue);
        }

    });
    contentPanel.add(hoverPanel, gBC);

    //Create Buttons
    JToggleButton mouseListenButton = new JToggleButton("Mouse Listen");
    mouseListenButton.addActionListener(new AbstractAction(){
        @Override
        public void actionPerformed(ActionEvent e) {
            GlowPanel.glowPanel.setStateMouseListen();
        }
    });
    gBC.gridy++;
    gBC.weighty = 1;
    gBC.gridwidth = 1;
    contentPanel.add(mouseListenButton, gBC);

    JToggleButton glowThreadButton = new JToggleButton("GlowThread Button");
    glowThreadButton.addActionListener(new AbstractAction(){
        @Override
        public void actionPerformed(ActionEvent e) {
            GlowPanel.glowPanel.setStateGlowThread();
        }
    });
    gBC.gridx++;
    contentPanel.add(glowThreadButton, gBC);

    JToggleButton offButton = new JToggleButton("Off Button");
    offButton.addActionListener(new AbstractAction(){
        @Override
        public void actionPerformed(ActionEvent e) {
            GlowPanel.glowPanel.setStateOff();
        }
    });
    gBC.gridx++;
    contentPanel.add(offButton, gBC);

    GlowPanel.buttonGroup = new ButtonGroup();
    GlowPanel.buttonGroup.add(mouseListenButton);
    GlowPanel.buttonGroup.add(glowThreadButton);
    GlowPanel.buttonGroup.add(offButton);


    jFrame.setContentPane(contentPanel);
    jFrame.setGlassPane(GlowPanel.glowPanel);
    jFrame.getGlassPane().setVisible(true);
    jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    jFrame.pack();
    jFrame.setVisible(true);
}

public static void main(String[] args){
    SwingUtilities.invokeLater(new Runnable(){
        public void run(){
            GlowPanel.createGUI();
        }
    });
}

@Override
public boolean isOptimizedDrawingEnabled(){
    return false;
}
//End Methods

public static class RepaintMouseAdapter extends MouseAdapter{
    private JComponent jC;

    public RepaintMouseAdapter(JComponent jC){
        this.jC = jC;
    }

    @Override
    public void mouseMoved(MouseEvent e){
        GlowPanel.p = e.getPoint();
        this.jC.repaint();
    }
}

public static class RedispatchAdapter extends MouseAdapter{
    private JComponent lastComponentEntered;


    @Override
    public void mouseClicked(MouseEvent e){
        this.dispatchEvent(e);
    }

    @Override
    public void mousePressed(MouseEvent e){
        this.dispatchEvent(e);
    }

    @Override
    public void mouseReleased(MouseEvent e){
        this.dispatchEvent(e);
    }

    @Override
    public void mouseEntered(MouseEvent e){
        this.dispatchEvent(e);
        System.out.println("MOUSE ENTERED");
    }

    @Override
    public void mouseExited(MouseEvent e){
        this.dispatchEvent(e);
    }

    @Override
    public void mouseWheelMoved(MouseWheelEvent e){
        this.dispatchEvent(e);
    }

    @Override
    public void mouseDragged(MouseEvent e){
        this.dispatchEvent(e);
    }

    @Override
    public void mouseMoved(MouseEvent e){
        this.dispatchEvent(e);
    }

    private void dispatchEvent(MouseEvent e) {
        Component glass = (Component) e.getSource();
        Point glassPanePoint = e.getPoint();
        Container container = glass.getParent();
        Point containerPoint = SwingUtilities.convertPoint(glass,
                glassPanePoint, container);

        if (containerPoint.y < 0) { // we're not in the content pane
            // Could have special code to handle mouse events over
            // the menu bar or non-system window decorations, such as
            // the ones provided by the Java look and feel.
        } else {
            // The mouse event is probably over the content pane.
            // Find out exactly which component it's over.
            JComponent component = (JComponent) GlowPanel.getDeepestComponentAt(
                    container, containerPoint.x, containerPoint.y);
            //System.out.println(component);
            if (component != null) {
                // Forward events to component below
                Point componentPoint = SwingUtilities.convertPoint(
                        glass, glassPanePoint, component);
                if(e instanceof MouseWheelEvent){
                    MouseWheelEvent wE = (MouseWheelEvent) e;
                    component.dispatchEvent(new MouseWheelEvent(component, wE.getID(), wE.getWhen(),
                            wE.getModifiers(), componentPoint.x, componentPoint.y, wE.getClickCount(),
                            wE.isPopupTrigger(), wE.getScrollType(), wE.getScrollAmount(), wE.getWheelRotation()));
                }   else{
                    component.dispatchEvent(new MouseEvent(component, e
                            .getID(), e.getWhen(), e.getModifiers(),
                            componentPoint.x, componentPoint.y, e
                                    .getClickCount(), e.isPopupTrigger()));
                }


                if(this.lastComponentEntered != null && !this.lastComponentEntered.equals(component)){
                    System.out.println(!this.lastComponentEntered.equals(component));
                }
                if(this.lastComponentEntered != null && !this.lastComponentEntered.equals(component)){
                    e.getComponent().setCursor(component.getCursor());
                    component.dispatchEvent(new MouseEvent(component, MouseEvent.MOUSE_ENTERED, e.getWhen(),
                            e.getModifiers(), componentPoint.x, componentPoint.y,
                            e.getClickCount(), e.isPopupTrigger()));
                    Point lastComponentPoint = SwingUtilities.convertPoint(
                            glass, glassPanePoint, this.lastComponentEntered);
                    this.lastComponentEntered.dispatchEvent(new MouseEvent(this.lastComponentEntered,
                            MouseEvent.MOUSE_EXITED, e.getWhen(),
                            e.getModifiers(), lastComponentPoint.x, lastComponentPoint.y,
                            e.getClickCount(), e.isPopupTrigger()));
                }
                this.lastComponentEntered = component;
                System.out.println(this.lastComponentEntered);
            }


        }
        e.getComponent().repaint();
    }

}

public static Component getDeepestComponentAt(Component parent, int x, int y) {
    if (!parent.contains(x, y)) {
        return null;
    }
    if (parent instanceof Container) {
        Component components[] = ((Container)parent).getComponents();
        for (Component comp : components) {
            if (comp != null && comp.isVisible() && !(comp instanceof GlowPanel) && !(comp instanceof JLabel)) {
                Point loc = comp.getLocation();
                if (comp instanceof Container) {
                    comp = getDeepestComponentAt(comp, x - loc.x, y - loc.y);
                } else {
                    comp = comp.getComponentAt(x - loc.x, y - loc.y);
                }
                if (comp != null && comp.isVisible()) {
                    return comp;
                }
            }
        }
    }
    return parent;
}

}

希望这行得通。我基本上将项目中的内容复制粘贴到示例类中。

【问题讨论】:

  • 您需要将鼠标事件重新发送到父容器 - for example
  • 对于它的价值,我会专注于使用 MouseMoitionListener 并通过玻璃窗格的 mouseMoved 事件跟踪鼠标位置并重新调度(转换后的)@987654325 @ 到父容器

标签: java swing


【解决方案1】:

我最终选择了制作自己的自定义 getDeepestComponentAt(确保组件不是 GlassPane/组件也包含侦听器),并且为我的 RedispatchAdapter 添加了 lastComponent 的实例字段。如果 lastComponent 与之前的不一样,则将 MOUSE_ENTERED 发送到新组件,将 MOUSE_EXIT 发送到旧组件。

我将编辑我的代码以显示结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-01-09
    • 1970-01-01
    • 1970-01-01
    • 2018-11-06
    • 1970-01-01
    • 2017-08-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多