在屏幕上检测鼠标移动

6
我创建了一个MouseMotionDetection类,它的作用只是检测用户是否在屏幕上移动了鼠标。
为此,我在我的类构造函数中创建了一个新的JFrame,并设置了屏幕大小,但它是不可见的,因此基本上我正在观察整个屏幕上的鼠标运动。
但是,我有一个奇怪的bug:
在当前代码的形式下,一旦激活此类,我只检测到一个鼠标运动,之后就没有其他反应了。但如果我将将设置框架背景为0f, 0f, 0f, 0f(透明)的行注释掉,然后再激活,整个屏幕就变成灰色,我可以跟踪所有的鼠标运动,就像我想要的那样(只是我什么也看不到)。
我真的不理解为什么会出现这种情况,也没有发现相关问题,甚至在与MouseMotion事件相关的javadoc中也没有提到。
以下是代码:
public class MouseMotionDetection extends JPanel
                implements MouseMotionListener{

public MouseMotionDetection(Region tableRegion, Observer observer){
    addMouseMotionListener(this);
    setBackground(new Color(0f,0f,0f,0f));
    JFrame frame = new JFrame();         
    frame.setUndecorated(true);
    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    frame.setSize(screenSize);
    frame.setBackground(new Color(0f,0f,0f,0f));
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    frame.setAlwaysOnTop(true);
    JComponent contentPane = this;
    contentPane.setOpaque(true);
    frame.getContentPane().add(contentPane, BorderLayout.CENTER);
    frame.setVisible(true);
}

@Override
public void mouseDragged(MouseEvent arg0) {

}

@Override
public void mouseMoved(MouseEvent arg0) {
    System.out.println("mouse movement detected");
}

我不会选择Java来编写这种类型的程序,而是会使用C或C++编写一个实用程序来挂接到操作系统的鼠标事件上,或者使用AutoIt这样的实用语言(如果是Windows平台)来完成此任务。 - Hovercraft Full Of Eels
2个回答

11

完全透明的框架不会接收鼠标事件。

这里有一种使用MouseInfo的替代方法。如果应用程序的组件是不可见(透明),未聚焦或最小化,则可以使用此方法。

输入图像描述

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;

public class MouseMoveOnScreen {

    Robot robot;
    JLabel label;
    GeneralPath gp;
    Dimension d = Toolkit.getDefaultToolkit().getScreenSize();

    MouseMoveOnScreen() throws AWTException {
        robot = new Robot();

        label = new JLabel();
        gp = new GeneralPath();
        Point p = MouseInfo.getPointerInfo().getLocation();
        gp.moveTo(p.x, p.y);
        drawLatestMouseMovement();
        ActionListener al = new ActionListener() {

            Point lastPoint;

            @Override
            public void actionPerformed(ActionEvent e) {
                Point p = MouseInfo.getPointerInfo().getLocation();
                if (!p.equals(lastPoint)) {
                    gp.lineTo(p.x, p.y);
                    drawLatestMouseMovement();
                }
                lastPoint = p;
            }
        };
        Timer timer = new Timer(40, al);
        timer.start();
    }

    public void drawLatestMouseMovement() {
        BufferedImage biOrig = robot.createScreenCapture(
                new Rectangle(0, 0, d.width, d.height));
        BufferedImage small = new BufferedImage(
                biOrig.getWidth() / 4,
                biOrig.getHeight() / 4,
                BufferedImage.TYPE_INT_RGB);
        Graphics2D g = small.createGraphics();
        g.scale(.25, .25);
        g.drawImage(biOrig, 0, 0, label);

        g.setStroke(new BasicStroke(8));
        g.setColor(Color.RED);
        g.draw(gp);
        g.dispose();

        label.setIcon(new ImageIcon(small));
    }

    public JComponent getUI() {
        return label;
    }

    public static void main(String[] args) throws Exception {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                JPanel ui = new JPanel(new BorderLayout(2, 2));
                ui.setBorder(new EmptyBorder(4, 4, 4, 4));

                try {
                    MouseMoveOnScreen mmos = new MouseMoveOnScreen();
                    ui.add(mmos.getUI());
                } catch (AWTException ex) {
                    ex.printStackTrace();
                }

                JFrame f = new JFrame("Track Mouse On Screen");
                // quick hack to end the frame and timer
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.setContentPane(ui);
                f.pack();
                f.setLocationByPlatform(true);
                f.setVisible(true);
            }
        };
        SwingUtilities.invokeLater(r);
    }
}

这太棒了,我不知道Java可以如此强大。你从哪里学到机器人类的知识的? - Some Guy
@SomeGuy 那是很久以前的事了,我已经记不清了。最有可能的是Java教程、Java文档或参与usenet新闻组。 - Andrew Thompson

3
我认为透明像素不会产生MouseEvents。也就是说,MouseEvent会被分派到帧下面的“可见”组件。
因此,要接收事件,您不能使用绝对透明度。但是,您可能可以使用alpha值为1。我怀疑在“透明框架”的绘制中不会注意到差异。
所以,我会使用以下代码:
//frame.setBackground(new Color(0f,0f,0f,0f));
frame.setBackground(new Color(0f, 0f, 0f, 1f));

由于您在将其添加到框架时将 "contentPane" 设置为透明,因此以下内容不需要:
//setBackground(new Color(0f,0f,0f,0f));

需要注意的是,这段代码只有在你的应用程序处于焦点状态时才会起作用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接