在Java中搜索没有焦点的输入

时间:2009-05-23 09:44:37

标签: java keylistener

我正在使用Robot类在Java中创建一个小程序。该程序接管鼠标。在调试过程中,如果它开始以我不想要的方式运行,很难退出程序,因为我无法将鼠标移动到eclipse中的终止按钮,我不能使用热键来点击它是因为鼠标在另一个窗口中不断点击,而是让窗口聚焦。

我想要做的就是连接一个keylistener,这样当我点击q时我可以退出该程序,但我知道如何做到这一点的唯一方法就是创建一个窗口,并且该窗口需要专注于捕获输入。有没有办法从任何地方听取键盘或鼠标输入,无论焦点是什么?

7 个答案:

答案 0 :(得分:21)

有一个图书馆为您辛勤工作: https://github.com/kwhat/jnativehook

答案 1 :(得分:13)

这不是一个微不足道的问题,Java也没有给你一个优雅的方法。您可以使用像banjollity建议的解决方案但是如果您的错误鼠标单击打开当前在任务栏中打开的另一个完整大小的窗口,那么即使这样也不会一直有效。

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

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

所以你想要做的是:

  1. 在C中实现一个程序,该程序将监听操作系统上的全局按键,如果此操作系统是Windows,则不要查找Windows钩子上的文档,这些文档已经在Web和其他地方得到了很好的文档记录。如果您的操作系统是Linux或Mac OS X,那么您将需要使用X11开发库来监听全局按键。根据我在http://ubuntuforums.org/showthread.php?t=864566撰写的Howto

  2. ,这可以在ubunutu linux发行版上完成
  3. 通过JNI将C代码连接到Java代码。这一步实际上是更容易的一步。按照我在http://ubuntuforums.org/showthread.php?t=864566的教程中使用的程序在windows和linux下进行操作,因为在两个操作系统上将C代码连接到Java代码的过程都是相同的。

  4. 要记住的重要一点是,如果您首先编写和调试C / C ++代码并确保它正常工作,则更容易使JNI代码正常工作。然后将它与Java集成很容易。

答案 2 :(得分:2)

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

颜色iconCenterColor =新颜色(255,0,0); //如果程序图标为红色

if(iconCenterColor.equals(robot.getPixelColor(10,15)))    抛出新的IllegalStateException(“机器人不与正确的应用程序交互。”);

要取消机器人,只需alt-tab到另一个应用程序。适用于简单的一款app驱动机器人。

答案 3 :(得分:1)

从终端的命令行启动程序,然后使用Ctrl-C终止它。

答案 4 :(得分:0)

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

答案 5 :(得分: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();
}

答案 6 :(得分:0)

(如 @MasterID 所述并在 JNativeHook's documentation 上显示,用于原生键盘输入检测 {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 默认会在您的控制台中显示很多您可能不想要的内容,要更改它,只需在您在主函数中使用的 try-catch 之前添加它即可显示(这也将关闭警告和错误消息,更多信息here):

//(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);
}

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