在Java中无焦点地侦听输入

在Java中无焦点地侦听输入,java,keylistener,Java,Keylistener,我正在使用Robot类用Java编写一个小程序。程序接管了鼠标。在调试过程中,如果它开始以我不希望的方式运行,则很难退出程序,因为在eclipse中我无法将鼠标移到终止按钮上,并且我不能使用热键来点击它,因为鼠标在另一个窗口中不断单击,从而使该窗口成为焦点 我想做的是连接一个keylistener,这样当我点击q时我就可以退出程序,但我知道如何做的唯一方法是创建一个窗口,该窗口需要焦点来捕获输入。有没有一种方法可以在任何地方监听键盘或鼠标的输入,而不管焦点是什么?从终端中的命令行启动程序,并使用

我正在使用Robot类用Java编写一个小程序。程序接管了鼠标。在调试过程中,如果它开始以我不希望的方式运行,则很难退出程序,因为在eclipse中我无法将鼠标移到终止按钮上,并且我不能使用热键来点击它,因为鼠标在另一个窗口中不断单击,从而使该窗口成为焦点


我想做的是连接一个keylistener,这样当我点击q时我就可以退出程序,但我知道如何做的唯一方法是创建一个窗口,该窗口需要焦点来捕获输入。有没有一种方法可以在任何地方监听键盘或鼠标的输入,而不管焦点是什么?

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

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

这不是一个小问题,Java并没有为您提供一种优雅的方式。您可以使用Banjolly建议的解决方案,但如果您错误的鼠标单击打开另一个当前在任务栏中打开的全尺寸窗口,则即使这样也不会一直有效

事实上,Java默认情况下对操作系统的控制很少。这是由于两个主要原因:安全性(正如java文档所描述的)和不同的操作系统处理事件完全不同的事实,并且使用一个统一的模型来表示所有这些可能没有多大意义

为了回答你的问题,我想你想要的是你的程序的某种行为,它在全球范围内监听按键,而不仅仅是在你的应用程序中。类似这样的东西需要您访问所选操作系统提供的功能,要用Java访问它,您需要通过Java本机接口(JNI)层进行访问

所以你想做的是:

  • 使用C语言实现一个程序,该程序将侦听操作系统上的全局按键,如果该操作系统是Windows,则可以在Windows钩子上查找文档,这些文档由Microsoft和MSDN在web和其他地方进行了很好的记录。如果您的操作系统是Linux或Mac OS X,那么您需要使用X11开发库监听全局按键。这可以在ubunutu linux发行版上根据我在

  • 通过JNI将C代码连接到Java代码。这一步实际上是更简单的一步。遵循我在windows和linux下的教程中使用的过程,因为将C代码连接到Java代码的过程在两个操作系统上都是相同的


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

    也有同样的问题。在我的例子中,机器人只控制一个Windows应用程序,这是最大化的。我把这些线放在驱动机器人的主回路的顶部:

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

    if(iconCenter.equals(robot.getPixelColor(10,15))) 抛出新的非法状态异常(“机器人未与正确的应用程序交互”)


    要取消机器人,只需将tab键切换到另一个应用程序。适用于简单的单应用驱动机器人。

    有一个库可以为您完成繁重的工作:

    这里有一个纯Java的方法来解决您描述的问题(不是KeyListener问题…使用robot问题时提前退出测试):

    在整个测试过程中,将鼠标位置与测试最近设置的鼠标位置进行比较。如果不匹配,请退出测试。注:此代码的重要部分是
    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();
    }
    
    (如本机键盘输入检测{}所述和所示)

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


    >请记住在项目中添加jnativehook库,以便能够使用其所有实用程序。此答案应标记为正确答案。显然,Java生态系统确实提供了一种简单的方法来优雅地完成这项任务。@CollinBell说,该库使用每个操作系统的本机方法来完成上述任务。还有其他库也做类似的事情。我认为说Java生态系统确实提供了一种简单的方法来实现这一点是不准确的。有人千方百计制作第三方库来完成此任务,它不是任何标准Java包的一部分。@Idog库是生态系统的一部分。我主要关心的是“不写任何低级代码也能完成吗?”答案是肯定的(ish)。我知道是因为我使用了这个库(尽管底层c代码中有一个bug,我必须修复它并提交一个pull请求。)在我看来,这是正确的答案。问题是,它只用于调试,完全不需要更改代码。我使用ALT+F4关闭Java窗口,同时使用“卡住”的机器人编写测试。测试完成并正常工作(生产准备就绪)后,不需要。
    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
    }
    }
    
    public void nativeKeyPressed(NativeKeyEvent e) {
        System.out.println("Key Pressed: " + NativeKeyEvent.getKeyText(e.getKeyCode()));
        if (e.getKeyCode() == NativeKeyEvent.VC_Q){
            System.exit(1);
        }
    }
    
    //(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);
    }