当我进入或退出JDialog时,为什么方法mouseExited和mouseEntered一起运行?

时间:2013-09-05 18:40:02

标签: java swing mouseevent jdialog

是的,这个问题已经发布在JavaRanch上,但我对它们并不了解。

我有JDialog,其中添加了MouseListener,如下所示:

super.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseExited(MouseEvent e) {
                if (e.getSource() instanceof ConnectionTreeTooltip) {
                    System.out.println("mouse exited!!!!!!!!!!!!!!!!!!=" + e);
                    // hideTooltip();
                }
            }

            @Override
            public void mouseEntered(MouseEvent e) {
                if (e.getSource() instanceof ConnectionTreeTooltip) {
                    System.out.println("mouse entered!!!!!!!!!!!!!!!!!!=" + e);
                    // hideTooltip();
                }
            }
        });

ConnectionTreeTooltip是这个jdialog。 问题是只要鼠标进入或退出JDialog,就会调用这两个方法。他们只是一起跑。

我的JDialog的代码:

public class ConnectionTreeTooltip extends JDialog {
...........
public ConnectionTreeTooltip(ConnectionsTree connectionsTree) {
        super(connectionsTree.getMainFrame(), "", false);
        super.setUndecorated(true);
        super.setFocusableWindowState(false);
.............
super.getContentPane().add(scrollPane);
        super.pack();
    }

connectionsTree.getMainFrame()返回JFrame的实例。

编辑以下是完整代码:

public ConnectionTreeTooltip(ConnectionsTree connectionsTree) {
        super(connectionsTree.getMainFrame(), "", false);
        super.setUndecorated(true);
        super.setFocusableWindowState(false);

        this.connectionsTree = connectionsTree;
        JPanel contentPane = (JPanel) super.getContentPane();
        contentPane.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED, Color.blue, Color.red));
        tipLabel = new JLabel();
        // by default, JLabel is not focusable.
        tipLabel.setFocusable(true);
        tipLabel.setBackground(Color.WHITE);
        scrollPane = new JScrollPane(tipLabel) {
            @Override
            public Dimension getPreferredSize() {
                return preferredSizeOfScrollPane;
            }
        };
        scrollPane.setBorder(BorderFactory.createEmptyBorder(3, 3, 1, 1));
        super.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseExited(MouseEvent e) {
                if (e.getSource() instanceof ConnectionTreeTooltip) {
                    System.out.println("mouse exited!!!!!!!!!!!!!!!!!!=" + e);
                    // hideTooltip();
                }
            }

            @Override
            public void mouseEntered(MouseEvent e) {
                if (e.getSource() instanceof ConnectionTreeTooltip) {
                    System.out.println("mouse entered!!!!!!!!!!!!!!!!!!=" + e);
                    // hideTooltip();
                }
            }
        });
        tipLabel.addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                // tipLabel can key events only if it is visible and focused
                if (e.getKeyCode() == 32) {
                    unFocusTipLabel();
                }
            }
        });
        super.getContentPane().add(scrollPane);
        super.pack();
    }

为什么我的JDialog或我的MouseListener出现了什么问题?

谢谢!

3 个答案:

答案 0 :(得分:2)

你的JDialog很可能在其中拥有一个从对话框本身窃取鼠标监听器的组件,所以当你的鼠标进入对话框时,对话框会检测到鼠标进入,但鼠标会立即进入对话框的组件(也许就是JScrollPane),mouselistener会感觉你已经离开了对话框并进入了它的子组件。

例如,我的SSCCE:

import java.awt.event.*;
import javax.swing.*;

public class MouseListenerTest {
   public static void main(String[] args) {
      final JFrame mainFrame = new JFrame("My Frame");
      mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

      mainFrame.add(new JPanel() {
         {
            add(new JButton(new AbstractAction("Show Dialog") {

               @Override
               public void actionPerformed(ActionEvent arg0) {
                  ConnectionTreeTooltip cttt = new ConnectionTreeTooltip(
                        mainFrame);
                  cttt.setVisible(true);
               }
            }));
         }
      });
      mainFrame.pack();
      mainFrame.setLocationRelativeTo(null);
      mainFrame.setVisible(true);

   }
}

class ConnectionTreeTooltip extends JDialog {

   public ConnectionTreeTooltip(JFrame mainFrame) {
      super(mainFrame, "", false);
      setUndecorated(true);
      setFocusableWindowState(false);
      add(new JScrollPane(new JTextArea(20, 40)));
      ((JPanel)getContentPane()).setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
      pack();
      addMouseListener(new MouseAdapter() {
         @Override
         public void mouseExited(MouseEvent e) {
            if (e.getSource() instanceof ConnectionTreeTooltip) {
               System.out.println("mouse exited!!!!!!!!!!!!!!!!!!=" + e);
               // hideTooltip();
            }
         }

         @Override
         public void mouseEntered(MouseEvent e) {
            if (e.getSource() instanceof ConnectionTreeTooltip) {
               System.out.println("mouse entered!!!!!!!!!!!!!!!!!!=" + e);
               // hideTooltip();
            }
         }
      });
   }
}

现在最困难的部分是弄清楚如何使用GlassPane来获取鼠标输入/离开信息,但仍允许将鼠标事件传递到玻璃窗格下方的对话框中。


修改

是的解决方案是使用玻璃窗格:

import java.awt.event.*;
import javax.swing.*;

public class MouseListenerTest {
   public static void main(String[] args) {
      final JFrame mainFrame = new JFrame("My Frame");
      mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

      mainFrame.add(new JPanel() {
         {
            add(new JButton(new AbstractAction("Show Dialog") {

               @Override
               public void actionPerformed(ActionEvent arg0) {
                  ConnectionTreeTooltip cttt = new ConnectionTreeTooltip(
                        mainFrame);
                  cttt.setVisible(true);
               }
            }));
         }
      });
      mainFrame.pack();
      mainFrame.setLocationRelativeTo(null);
      mainFrame.setVisible(true);

   }
}

class ConnectionTreeTooltip extends JDialog {

   public ConnectionTreeTooltip(JFrame mainFrame) {
      super(mainFrame, "", false);
      setUndecorated(true);
      setFocusableWindowState(false);
      add(new JScrollPane(new JTextArea(20, 40)));
      ((JPanel) getContentPane()).setBorder(
            BorderFactory.createEmptyBorder(4, 4, 4, 4));
      pack();

      JComponent glassPane = (JComponent) getGlassPane();
      glassPane.setVisible(true);

      glassPane.addMouseListener(new MyMouseAdapter());
   }

   private class MyMouseAdapter extends MouseAdapter {

      @Override
      public void mouseExited(MouseEvent e) {
         System.out.println("mouse exited");
      }

      @Override
      public void mouseEntered(MouseEvent e) {
         System.out.println("mouse entered");
      }
   }
}

答案 1 :(得分:1)

我以比使用GlassPane更好的方式解决了我的问题。 GlassPane只给我增加了麻烦,因为我无法滚动GlassPane下的JScrollPane,并且在glasspane swing教程中建议的redispatching mouse事件根本没有帮助,因为glasspane上的mouseListener没有收到mouseDragged。

解决方案是使用AWTEventListener。使用AWTEventListener,我可以检查组件事件是否打开以及事件的ID。所以我的AWTEventListener检查是否在JDialog上发生了mouseEntered事件,如果是,则以编程方式聚焦。如果在JScrollPane上发生了mouseDragged事件,我会以编程方式滚动它。最后,如果在JDialog上发生mouseExited事件,它将被隐藏。

Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
            @Override
            public void eventDispatched(AWTEvent event) {
                MouseEvent me = (MouseEvent) event;
                Component mouseComponent = me.getComponent();
                if (mouseComponent == scrollPane) {
                    if (me.getID() == MouseEvent.MOUSE_DRAGGED) {
                         scrollPane.getViewport().setViewPosition(me.getPoint());  // scroll JScrollPane programmatically
                    }
                } else if (mouseComponent == ConnectionTreeTooltip.this) {
                    if (me.getID() == MouseEvent.MOUSE_ENTERED)
                        focusTipLabel(); //focus JDialog(ConnectionTreeTooltip)
                    else if (me.getID() == MouseEvent.MOUSE_EXITED) {
                        hideTooltip(); //hide JDialog(ConnectionTreeTooltip)
                    }
                }
            }
        }, AWTEvent.MOUSE_EVENT_MASK);

但是这个AWTEventListener适用于从主框架中的任何组件生成的mouseevents。它并不总是处理JDialog上的mouseExited事件。

答案 2 :(得分:0)

或者更简单的替代方案。检查mouseExited上鼠标的坐标,如果鼠标真的在控件之外,则只计为退出:

super.addMouseListener(new MouseAdapter() {
    @Override
    public void mouseExited(MouseEvent e) {

        Point cursor = e.getPoint();
        if ( (cursor.x < super.getX()) || (cursor.y < super.getY()) || (cursor.x > popup.getX()+super.getWidth()) || (cursor.y > super.getY()+super.getHeight()))
            popup.setVisible(false);
    }
});