25

我正在使用 Robot 类在 Java 中制作一个小程序。程序接管鼠标。在调试过程中,如果它开始以我不希望的方式运行,则很难退出程序,因为我无法将鼠标移到 Eclipse 中的终止按钮,也无法使用热键点击它是因为鼠标在另一个窗口中不断单击,而是给该窗口焦点。

我想做的只是连接一个按键监听器,这样当我点击 q 时我可以退出程序,但我知道如何做到这一点的唯一方法是制作一个窗口,并且该窗口需要焦点来捕获输入。有没有办法从任何地方监听键盘或鼠标输入,而不管焦点是什么?

4

7 回答 7

33

有一个库可以为您完成艰苦的工作: https ://github.com/kwhat/jnativehook

于 2013-10-20T17:59:17.637 回答
14

这不是一个微不足道的问题,Java 并没有给你一种优雅的方法。您可以使用类似 banjollity 建议的解决方案,但如果您错误的鼠标点击打开另一个当前在任务栏中打开的全尺寸窗口,即使这样也不会一直有效。

事实是,默认情况下,Java 几乎没有给开发人员对操作系统的控制权。这是由于两个主要原因:安全性(正如 java 文档所引用的那样)以及不同的操作系统处理事件完全不同的事实,并且使用一个统一的模型来表示所有这些可能没有多大意义。

所以为了回答你的问题,我想你想要的是你的程序的某种行为,它在全球范围内监听按键,而不仅仅是在你的应用程序中。像这样的事情将要求您访问您选择的操作系统提供的功能,并且要在 Java 中访问它,您将需要通过 Java 本机接口 (JNI) 层来完成它。

所以你想做的是:

  1. 如果此操作系统是 Windows,则在 C 中实现一个程序,该程序将侦听您的操作系统上的全局按键,而不是在 Windows 挂钩上查找文档,该文档由 Microsoft 和 MSDN 在 web 和其他地方很好地记录。如果您的操作系统是 Linux 或 Mac OS X,那么您将需要使用 X11 开发库来监听全局按键。这可以根据我在http://ubuntuforums.org/showthread.php?t=864566上写的 Howto 在 ubunutu linux 发行版上完成

  2. 通过 JNI 将 C 代码连接到 Java 代码。这一步实际上是更容易的一步。遵循我在教程中使用的过程http://ubuntuforums.org/showthread.php?t=864566在 windows 和 linux 下,因为将 C 代码连接到 Java 代码的过程在两个操作系统上都是相同的。

要记住的重要一点是,如果您首先编写和调试您的 C/C++ 代码并确保它正常工作,那么让您的 JNI 代码工作起来会容易得多。然后将它与 Java 集成很容易。

于 2009-05-24T19:05:20.467 回答
2

有同样的问题。就我而言,机器人只控制了一个已最大化的 Windows 应用程序。我将这些行放在驱动机器人的主循环的顶部:

颜色 iconCenterColor = new Color(255,0,0); // 如果程序图标是红色的

if (iconCenterColor.equals(robot.getPixelColor(10,15))) throw new IllegalStateException("机器人没有与正确的应用程序交互。");

要取消机器人,只需 alt-tab 到另一个应用程序。非常适合一个简单的应用程序驾驶机器人。

于 2010-10-05T21:10:01.870 回答
1

从终端中的命令行启动程序并使用 Ctrl-C 终止它。

于 2009-05-23T21:17:51.557 回答
0

让您的程序打开第二个窗口,该窗口显示在主窗口下方但已最大化,然后您的错误鼠标点击将全部被最大化的窗口接收,并且它可以接收您的键盘输入。

于 2009-05-23T21:32:32.933 回答
0

这是解决您所描述的问题的纯 Java 方法(不是 KeyListener 问题......使用机器人问题时尽早退出测试):

在整个测试过程中,将鼠标位置与您的测试最近设置的位置进行比较。如果不匹配,请退出测试。注意:此代码的重要部分是testPosition方法。这是我最近使用的代码:

public void testSomething() throws Exception {
    try {
        // snip

        // you can even extract this into a method "clickAndTest" or something
        robot.mouseMove(x2, y2);
        click();
        testPosition(x2, y2);

        // snip
    } catch (ExitEarlyException e) {
        // handle early exit
    }
}

private static void click() throws InterruptedException {
    r.mousePress(InputEvent.BUTTON1_DOWN_MASK);
    Thread.sleep(30 + rand.nextInt(50));
    r.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
    Thread.sleep(30 + rand.nextInt(50));
}

private static void testPosition(int x2, int y2) throws ExitEarlyException {
    Point p = MouseInfo.getPointerInfo().getLocation();
    if(p.x != x2 || p.y != y2) throw new ExitEarlyException();
}
于 2014-08-05T15:16:34.460 回答
0

(正如@MasterID提到的,并在JNativeHook 的本地键盘输入检测文档中显示 { main GitHub project here }),

此代码应该足以在没有应用程序焦点(按下和/或释放)的情况下收听任何键

>>记得在你的项目中添加 jnativehook 库,以便能够使用它的所有实用程序。<<

public class yourClass implements NativeKeyListener {//<-- Remember to add the jnativehook library

public void nativeKeyPressed(NativeKeyEvent e) {
    System.out.println("Key Pressed: " + NativeKeyEvent.getKeyText(e.getKeyCode()));
}

public void nativeKeyReleased(NativeKeyEvent e) {
        System.out.println("Key Released: " + NativeKeyEvent.getKeyText(e.getKeyCode()));
}

public void nativeKeyTyped(NativeKeyEvent e) {
        System.out.println("Key Typed: " + NativeKeyEvent.getKeyText(e.getKeyCode()));
}

public static void main(String args[]){
    //Just put this into your main:
    try {
        GlobalScreen.registerNativeHook();
    }
    catch (NativeHookException ex) {
        System.err.println("There was a problem registering the native hook.");
        System.err.println(ex.getMessage());
        System.exit(1);
    }
    
    GlobalScreen.addNativeKeyListener(new yourClass());
    //Remember to include this^                     ^- Your class
}
}

对于这个特殊的问题,使用 nativeKeyPressed 方法,如下所示:

public void nativeKeyPressed(NativeKeyEvent e) {
    System.out.println("Key Pressed: " + NativeKeyEvent.getKeyText(e.getKeyCode()));
    if (e.getKeyCode() == NativeKeyEvent.VC_Q){
        System.exit(1);
    }
}

请注意,默认情况下,JNativeHook 在您的控制台中显示了很多您可能不想要的东西,要更改它,只需在您在 main 函数中使用的 try-catch 之前添加它,如图所示(这也将关闭警告和错误消息,更多信息在这里):

//(From here)
Logger logger = Logger.getLogger(GlobalScreen.class.getPackage().getName());
logger.setLevel(Level.OFF);
logger.setUseParentHandlers(false);
//(To there-^)
try {
    GlobalScreen.registerNativeHook();
}
catch (NativeHookException ex) {
    System.err.println("There was a problem registering the native hook.");
    System.err.println(ex.getMessage());
    System.exit(1);
}

免责声明:我知道这个问题在几年前就已经解决了,我只是希望有人发现这个问题更容易找到/使用。

于 2020-12-20T03:27:25.670 回答