2009-11-20 92 views
6

我有一个问题,Swing(在Java 1.6,Windows中)似乎没有以我想要的方式触发mouseEntered和mouseExited事件。我有一个应用程序,我希望在JScrollPane中有多个JPanel垂直堆叠,当鼠标悬停在它们上方时,它们应该用不同的颜色突出显示。足够简单的问题,但是每当我使用鼠标滚轮滚动时,它都不会表现得很好。鼠标滚轮事件后,Swing不会正确触发mouseEntered/mouseExited吗?

我做了一个示例应用程序来说明我的问题(代码见下文)。下面的图片来自那个,而不是“真正的”应用程序。

当我将鼠标光标放在面板的边缘上时,它会正确突出显示。现在,当我使用鼠标滚轮向下滚动时,我希望光标位于框B之上,并且触发正确的mouseEntered/mouseExited事件,以便A变白并且B变红。

alt text http://perp.se/so/1.png

alt text http://perp.se/so/2.png

然而,这似乎并没有发生。

现在,如果我触发另一个鼠标事件,例如“移动1像素”,“单击按钮”或“滚动另一个步骤”,B会变亮。知道这一点,我或许可以用一种诡异的方式解决它,但我宁愿不,如果有适当的解决方案。

所以基本上我想知道的是,如果这被认为是Swing中的错误,或者我只是在做错事情?

import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Font; 
import java.awt.GridBagConstraints; 
import java.awt.GridBagLayout; 
import java.awt.GridLayout; 
import java.awt.Insets; 
import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 
import javax.swing.BorderFactory; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.JScrollPane; 
import javax.swing.SwingConstants; 
import javax.swing.SwingUtilities; 

public class ScrollTest extends JFrame { 

    public static class LetterPanel extends JPanel { 

     private static final Font BIG_FONT = new Font(Font.MONOSPACED, Font.BOLD, 24); 

     public LetterPanel(String text) { 
      setBackground(Color.WHITE); 
      setBorder(BorderFactory.createLineBorder(Color.BLACK)); 

      addMouseListener(new MouseAdapter() { 

       @Override 
       public void mouseEntered(MouseEvent e) { 
        setBackground(Color.RED); 
       } 

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

      setLayout(new GridLayout(1, 1)); 
      setPreferredSize(new Dimension(-1, 50)); 

      JLabel label = new JLabel(text, SwingConstants.CENTER); 
      label.setFont(BIG_FONT); 
      add(label); 
     } 
    } 

    public ScrollTest() { 
     setLayout(new GridLayout(1, 1)); 
     setSize(400, 400); 

     JPanel base = new JPanel(); 

     JScrollPane jsp = new JScrollPane(base); 
     jsp.getVerticalScrollBar().setUnitIncrement(16); 
     add(jsp); 

     base.setLayout(new GridBagLayout()); 
     GridBagConstraints gbc = new GridBagConstraints(); 
     gbc.gridx = 0; 
     gbc.gridy = 0; 
     gbc.gridheight = 1; 
     gbc.gridwidth = 1; 
     gbc.fill = GridBagConstraints.HORIZONTAL; 
     gbc.insets = new Insets(0, 0, 10, 0); 
     gbc.weightx = 1.0; 

     for (char c = 'A'; c <= 'Z'; c++) { 
      base.add(new LetterPanel(String.valueOf(c)), gbc); 
      gbc.gridy++; 
     } 
    } 

    public static void main(String[] args) { 
     final JFrame f = new ScrollTest(); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

     SwingUtilities.invokeLater(new Runnable() { 

      public void run() { 
       f.setVisible(true); 
      } 
     }); 
    } 
}
+1

限制是有原因的。没有它,SO会有相当多的SPAM帖子。我添加了这些图片,并删除了关于如何“阻碍”这些规则的说法。 – 2009-11-20 09:30:52

+0

除了这些评论之外,你还提出了一个很好的问题:清晰的图像,一段好的代码片段:做得很好。我不是Swing大师,但稍后我会看看它(如果到那时还没有回答)。 +1 – 2009-11-20 09:32:38

+0

感谢您的编辑。首先与这个问题作斗争,写下问题,编写代码片段,拍好截图,将它们上传到服务器,然后在我的帖子中添加SO barf,仅仅因为我是一个新用户而感到有点沮丧。我现在感觉好多了。 :-) – perp 2009-11-20 10:21:55

回答

5

这似乎与Tooltips and Scrollpanes中描述的类似。也就是说,没有鼠标事件产生,因为鼠标本身不移动,视口移动。我不确定其他使用AdjustmentListener在鼠标位置跟踪组件的确切解决方案。每次发生更改时,都可以将mouseExited事件触发到上一个面板,并将mouseEntered事件触发到新面板。

3

我可以让你的代码可靠地重现这一点,但只有当我没有完成滚动。在我的鼠标上,当鼠标滚轮完成滚动时,至少有一种“捕捉”。如果我滚动得非常缓慢,我可以让它移动,但它不会改变高光,直到鼠标轮到达“catch”。

当我这样做时,鼠标输入消息在前一面板上接收到(您看到的行为与此相同)。

看着它我滚动鼠标,它实际上并没有收到退出/输入的事件,除非我滚动到足以使鼠标滚轮“捕捉”。有可能Windows不会将消息发送给Java,直到发生“catch”为止......从我的测试中看,它就是它的样子。

您可能想要查看MouseWheelListener接口和MouseInfo类。我想你可能能够检测到轮子的运动,然后用MouseInfo.getPointerInfo()。getLocation()找出你的位置,然后找出你所在的组件并改变高亮。

+0

好奇......你的“Catch”可能足够粗糙,它会将鼠标移动一点,导致鼠标事件通过。您可以尝试将鼠标底部对准空气,使光线无法牵引。也可能是您的滚轮导致按钮3向下或某些东西不会被注意到。 – 2013-03-04 18:37:32