6

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

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

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

替代文字
(来源:perp.se

替代文字
(来源:perp.se

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

现在,如果我触发另一个鼠标事件,无论是“移动 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);
            }
        });
    }
}
4

2 回答 2

5

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

于 2009-11-20T21:15:49.417 回答
3

我可以让您的代码可靠地重现这一点,但前提是我还没有完全完成滚动。当鼠标滚轮完成滚动时,至少在我的鼠标上会有一种“捕捉”。如果我滚动得很慢,我可以让它移动,但在鼠标滚轮到达“catch”之前它不会改变突出显示。

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

看着它,我滚动鼠标,它实际上并没有接收到退出/输入的事件,除非我滚动到足以让鼠标滚轮“抓住”。在“捕获”发生之前,Windows 可能不会向 Java 发送消息……从我的测试来看,这就是它的样子。

您可能想要查看 MouseWheelListener 接口和 MouseInfo 类。我猜你可能能够检测到轮子的移动,然后用 MouseInfo.getPointerInfo().getLocation() 找出你在哪里,然后找出你在哪个组件上并更改突出显示。

于 2009-11-20T11:01:55.833 回答