如何在Java中获取窗口外的鼠标点击坐标

25

我需要使用Swing实现一个类,用于在用户单击屏幕上任何位置时获取鼠标坐标。如果我想要获取自己窗口内的鼠标坐标,我会使用MouseListener,但我希望它即使在用户单击程序外部也能起作用。

我希望我的类的行为就像KColorChooser:用户单击下拉按钮后,可以单击屏幕上任何位置以获取该点的颜色。但我不知道是否可以使用纯Java实现。

12个回答

28

虽然有限制,但是可以实现:

添加一个AWTEventListener来监听焦点事件。只要在按钮被点击前你的应用程序具有焦点,你就会接收到失去焦点事件。然后查询指针位置。

不过这种方法的限制在于,当然,你的应用程序会失去焦点。因此根据你最终想要实现的目标,这可能没有用。

如果你不想失去焦点,那么你将不得不暂时截取整个屏幕的截图,并在一个填充屏幕的窗口中显示它,该窗口像通常一样监听鼠标点击事件。

第一种方法的证明:

import java.awt.AWTEvent;
import java.awt.MouseInfo;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;

import javax.swing.JFrame;

public class Application1 {
    public static void main(String[] args) {
        Toolkit.getDefaultToolkit().addAWTEventListener(
          new Listener(), AWTEvent.MOUSE_EVENT_MASK | AWTEvent.FOCUS_EVENT_MASK);
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    private static class Listener implements AWTEventListener {
        public void eventDispatched(AWTEvent event) {
            System.out.print(MouseInfo.getPointerInfo().getLocation() + " | ");
            System.out.println(event);
        }
    }
}

点击应用程序外部会产生:

java.awt.Point[x=198,y=59] | java.awt.event.MouseEvent[MOUSE_EXITED, ...
java.awt.Point[x=976,y=503] | java.awt.FocusEvent[FOCUS_LOST, ...

第二点是应用程序之外的。


2
这实际上非常聪明,但它当然只会报告应用程序外的第一次单击,即导致失去焦点的那次单击。之后,除非应用程序再次获得焦点,否则不会报告任何其他单击。现在我想知道是否可能通过焦点请求来响应FOCUS_LOST事件...?! - Thomas
我认为,focus请求或window.toFront()请求不能保证影响VM之外的事物。在OSX上测试,我发现这并不起作用。 - Keilly
另一个缺点是您无法区分由于在窗口外单击和其他类型(例如,CTRL-Tabbing到另一个正在运行的应用程序)而导致的焦点丢失。 - Thomas
我已经根据自己的需求调整了这个解决方案,现在它可以满足我的要求了 :-) - cd1

24

别再考虑使用GlassPane了,有另一种100%本地化的Java方式可以在OS X和Windows系统上运行。

Java一直支持在OS X中为其窗口进行半透明处理,现在Java也支持在Windows系统上为其窗口进行半透明处理(自Java 1.6.0_10版本以来,需要检查版本)。

因此,关键就是:在单击“选择颜色”工具后,创建一个近乎透明的无边框Java窗口覆盖整个屏幕。将其Alpha设置为10(Alpha从0到255)。那个透明度非常低,用户不会注意到整个屏幕上有一个非常细的“几乎透明但只是非常非常非常透明”的无边框窗口。

现在当用户单击覆盖整个屏幕的“Alpha设置为10的透明无边框窗口”时,您就可以获得(x,y)坐标。

舍弃无边框Java窗口。

使用RobotgetRgb(x,y) 方法即可。

为什么将Alpha值设置为10而不是0?因为否则,点击事件不会被Java拦截,而是直接传递给操作系统(至少在OS X上是这样的)。存在一个阈值,我知道它不是“1”或“2”,大概在10左右。

编辑:我刚意识到您需要选择多个颜色,这更加棘手,但仍然可以使用100%的Java来完成。您可以接受“略微偏离”的颜色(受“几乎透明”的“隐形”层影响),或者在获取单击后,必须删除该层,获取正确的像素颜色,然后再次放置一个“近乎透明”的层。现在当然这是一种非常巧妙的方法,但可以使用100%的Java来实现。


非常希望这个类也能在Linux上执行。 - cd1

14

使用

import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;

PointerInfo inf = MouseInfo.getPointerInfo();
Point p = inf.getLocation();

p.x和p.y将给出窗口之外的坐标。


这是绝对完美的! - J Atkin
11
可以,但它不会显示点击发生的时间。 - Stefan Reich

5

我不知道是否可以使用纯Java实现。

这是不可能的,因为Java只能识别属于Java的Windows上的MouseEvents。


可以的(请看我的回答)。 - Stefan Reich

5

这些事件被定向到具有焦点的窗口,从桌面上的所有事件中,您只能获取鼠标位置。

正如Keilly已经展示的那样,只有获取鼠标位置是可能的。

您需要包含一个本地库


在Jotschi(键盘/鼠标钩子的原始作者)的许可下,我多年前拿到了他的库并创建了一个新版本。请在GitHub上找到最新版本和示例。还可以在博客文章中找到该库的描述。 - Kristian Kraljic

3

我个人没有尝试过,但也许您可以创建一个全屏的、透明的面板/框架等,并向其添加MouseListener。


@Chris:这是可能的,但有一些需要注意的地方(请参见我的答案),因为并非每个版本的Windows都支持完全透明的JFrame等。另一个问题是,在OS X上,一个真正“完全透明”的JFrame不会拦截点击事件。 - SyntaxT3rr0r

2

有一个小技巧可以实现。应该是100%跨平台的(在Linux和Windows上测试过)。基本上,您创建一个小的JWindow,使其“alwaysOnTop”并使用计时器移动鼠标。

详情请参见我的答案这里


1
通过命令行参数提供每次点击的位置(x,y)和时间间隔(d)。以下是程序:
import java.awt.* ;
import java.util.* ;

public final class ClickMouse extends TimerTask {
    public static int x, y, d ;

    public static void main(String[] args) {
        TimerTask clikMouse = new ClickMouse();
        Timer t = new Timer();
/*  
    x = Integer.parseInt(args[0]) ;
    y = Integer.parseInt(args[1]) ;
    d = Integer.parseInt(ares[2]) ;
*/
        x = 500;
        y = 200;
        d = 5;
        t.schedule(clikMouse,1000,d*1000);
    }

    public void run() {
        try 
        {
            Robot bot = new Robot();

            bot.mouseMove(x,y);
            bot.mousePress(java.awt.event.InputEvent.BUTTON1_MASK );
            bot.mouseRelease(java.awt.event.InputEvent.BUTTON1_MASK);
        }
        catch (Exception e)
        {
            System.out.println("Exception occured :" + e.getMessage());
        }
    }
}

1

看,我知道我晚了7年...

这是Keilly答案的重新制作,它允许在任何地方获取鼠标按钮点击的时间。主要问题是全屏游戏总是没有焦点,处理起来很烦人。

以下是代码:

import java.awt.AWTEvent;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;

import javax.swing.JFrame;

public class Main {

    public static JFrame frame = new JFrame();

    public static void main(String[] args) {
        Toolkit.getDefaultToolkit().addAWTEventListener(
          new Listener(), AWTEvent.MOUSE_EVENT_MASK | AWTEvent.FOCUS_EVENT_MASK);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        frame.setAlwaysOnTop(true);
        frame.setLocation(1, 1);
    }

    private static class Listener implements AWTEventListener {
        public void eventDispatched(AWTEvent event) {

            // We do not want the event to show twice,
            // as it shows for focusing and unfocusing

            if(event.getID() == 1004) {
                Point p = MouseInfo.getPointerInfo().getLocation();
                System.out.println("Mouse Clicked at " + p.x + ", " + p.y);
            }

            // The frame was just unfocused! To make
            // sure we get the next mouse click, we
            // need to focus it again!

            frame.setVisible(true);

        }
    }
}

1

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