如何在不破坏默认实现的情况下为JPanel实现MouseWheelListener?

只是; 我在JScrollPane有一个JPanel ;

正如所料; 默认情况下, JScrollPane侦听MouseWheelEvent以便滚轮在轮子旋转时以及光标hover在JPanel

在那之后; 我刚刚更新了JPanel以便它实现MouseWheelListener ,我为JPanel本身添加了这个鼠标滚轮监听器。

 @Override public void mouseWheelMoved(MouseWheelEvent e) { if (e.isControlDown()) { if (e.getWheelRotation() < 0) { System.out.println("mouse wheel Up"); } else { System.out.println("mouse wheel Down"); } } } 

JPanel响应这个实现的时间; 当光标hover在JPanel时,按下Ctrl键并且滚轮正在旋转。 但JScrollPane的默认行为意外丢失!

当我在光标hover在JPanel时旋转滚轮时, JScrollPane的滚动没有响应!

看起来; MouseWheelListener这个实现破坏了JPanel的默认值。

所以; 如何在不破坏默认实现的情况下为JPanel实现MouseWheelListener

MouseWheelEvents和滚动行为有一些微妙的警告。

例如,当您打开此页面(我的意思是您正在阅读的那个页面)时,将鼠标放在中间,然后开始使用滚轮向下滚动,您将滚动代码段。 请注意, 尽管代码段包含在具有滚动条的代码块中,但连续旋转鼠标滚轮不会触发代码块中的滚动,而只会触发整个页面。 但是当你在鼠标移动代码块时移动它, 然后滚动鼠标滚轮,那么你只会滚动代码块 – 而不是整个页面。

类似地,旋转鼠标滚轮可能不会影响hover的可滚动。 我认为这取决于Window Manager和Look&Feel,但在某些情况下,您将滚动包含焦点组件的滚动窗格 – 即使鼠标光标位于此组件之外,即使它位于可分辨的上方组件(例如,您也可以在Windows资源管理器中观察它)!


但是,其中一些机制和细微之处也可以在Swing组件中找到。 例如,如果组件本身没有处理它们,则会将MouseWheelEvents传递给祖先的redispatching机制 。

遵循这种模式,解决方案(在概念上类似于LuxxMiner提出的解决方案,但可能更通用)可能只是将MouseWheelEvent重新分配给父组件:

 package stackoverflow; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.SwingUtilities; public class MouseWheelListenerForPanelInScrollpane { public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { createAndShowGUI(); } }); } private static void createAndShowGUI() { JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.getContentPane().setLayout(new GridLayout(1,2)); MouseWheelListenerPanel m = new MouseWheelListenerPanel(); m.setPreferredSize(new Dimension(100,4000)); JScrollPane scrollPane = new JScrollPane(m); f.getContentPane().add(scrollPane); f.setSize(500,500); f.setLocationRelativeTo(null); f.setVisible(true); } } class MouseWheelListenerPanel extends JPanel implements MouseWheelListener { MouseWheelListenerPanel() { addMouseWheelListener(this); } @Override public void mouseWheelMoved(MouseWheelEvent e) { if (e.isControlDown()) { if (e.getWheelRotation() < 0) { System.out.println("mouse wheel Up"); } else { System.out.println("mouse wheel Down"); } } else { getParent().dispatchEvent(e); } } } 

如果ctrl 关闭,则添加else以直接将事件重新分派到滚动窗格:

 @Override public void mouseWheelMoved(MouseWheelEvent e) { if (e.isControlDown()) { if (e.getWheelRotation() < 0) { System.out.println("mouse wheel Up"); } else { System.out.println("mouse wheel Down"); } } else { // pass the event on to the scroll pane getParent().dispatchEvent(e); } } 

我不知道这是否真的有资格作为一个正确的答案,因为它是一种解决方法,但我想出了以下解决方案:只有在没有按下Ctrl时才调用scrollPanemouseWheelMoved方法:

 if (e.isControlDown()) { if (e.getWheelRotation() < 0) { infoLabel.setText("Mouse Wheel Up"); } else { infoLabel.setText("Mouse Wheel Down"); } } else { scrollPane.getListeners(MouseWheelListener.class)[0].mouseWheelMoved(e); } 

完整示例:

 import java.awt.BorderLayout; import java.awt.EventQueue; import java.awt.GridLayout; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.border.TitledBorder; public class Example { public Example() { JFrame frame = new JFrame(); frame.setLayout(new BorderLayout()); frame.add(new ScrollPanePanel()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 400); frame.setVisible(true); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { new Example(); } }); } } class ScrollPanePanel extends JPanel implements MouseWheelListener { private JLabel infoLabel; private JScrollPane scrollPane; public ScrollPanePanel() { JPanel panel = new JPanel(new GridLayout(0, 1)); for (int i = 1; i <= 100; i++) { panel.add(new JLabel("Label " + i)); } panel.addMouseWheelListener(this); scrollPane = new JScrollPane(panel); infoLabel = new JLabel(" "); JPanel infoPanel = new JPanel(); infoPanel.add(infoLabel); setLayout(new BorderLayout()); add(scrollPane); add(infoPanel, BorderLayout.SOUTH); } @Override public void mouseWheelMoved(MouseWheelEvent e) { if (e.isControlDown()) { if (e.getWheelRotation() < 0) { infoLabel.setText("Mouse Wheel Up"); } else { infoLabel.setText("Mouse Wheel Down"); } } else { scrollPane.getListeners(MouseWheelListener.class)[0].mouseWheelMoved(e); } } }