【问题标题】:how to obtain mouse click coordinates outside my window in Java如何在Java中获取我的窗口外的鼠标点击坐标
【发布时间】:2010-03-10 18:38:02
【问题描述】:

我需要实现一个类,使用Swing,它可以在用户点击屏幕任意位置时获取鼠标坐标。如果我想在我自己的窗口中获取鼠标坐标,我会使用MouseListener,但我希望即使用户在我的程序之外点击它也能工作。

我希望我的班级表现得像KColorChooser:用户单击下拉按钮,然后他可以单击屏幕上的任意位置以获取该点的颜色。但我不知道这是否可以使用纯 Java。

【问题讨论】:

    标签: java swing mouse mouseclick-event


    【解决方案1】:

    这是可能的,虽然有限:

    为焦点事件添加 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, ...
    

    第二点在应用之外。

    【讨论】:

    • 这其实很聪明,但它当然只会报告应用程序外部的第一次点击,也就是真正导致失去焦点的点击。之后,除非应用程序再次重新获得焦点,否则不会报告其他点击。现在我想知道是否可以通过焦点请求对 FOCUS_LOST 事件做出反应......?!
    • 我不这么认为,焦点请求或 window.toFront() 请求不能保证会影响 VM 之外的东西。在 OSX 上测试我发现这不起作用。
    • 另一个缺点是您将无法区分由于在窗口外单击和其他类型(例如,CTRL-Tabbing 到另一个正在运行的应用程序)导致的焦点丢失。
    • 我已经根据自己的情况调整了这个解决方案,现在它可以满足我的需要 :-)
    【解决方案2】:

    忘记GlassPane,还有另一种 100% 原生 Java 方法可以在 OS X 和 Windows 上运行。

    Java 在 OS X 上始终支持其窗口的半透明,Java 现在也支持其在 Windows 上的窗口的半透明(因为 Java 1.6.0_10 左右,需要检查)。

    所以诀窍是:单击“选择颜色” 工具后,您将创建一个覆盖整个屏幕的几乎透明的无边框 Java 窗口。您将其 alpha 设置为 10(alpha 从 0 到 255)。 Alpha 值非常低,用户不会注意到有一个非常薄的“几乎透明但只有非常非常非常透明”无边框窗口覆盖整个屏幕。

    现在,当用户点击覆盖整个屏幕的“alpha 设置为 10 的半透明无边框窗口”时,您会得到 (x,y)。

    放弃无边框的 Java 窗口。

    使用RobotgetRgb(x,y) 就完成了。

    为什么将 alpha 设置为 10 而不是 0?因为否则点击不会被 Java 拦截,而是直接转到操作系统(至少对于 OS X 上的事实来说,它是这样工作的)。有一个门槛,我知道它没有设置为“1”,也没有设置为“2”,大约是 10 左右。

    编辑我刚刚意识到你知道需要选择几种颜色,这比较棘手,但仍然可以使用 100% Java 来完成。要么您可以使用“略微关闭”的颜色(受“几乎透明”“不可见”层的影响)在获得点击后,您必须移除该层,获取正确的像素颜色,然后重新放置“几乎透明”的层。现在当然这是一个 hack,但它可以用 100% Java 完成。

    【讨论】:

    • 这个类也可以在Linux上执行是非常可取的。
    【解决方案3】:

    使用

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

    p.x 和 p.y 会给你窗外的坐标。

    【讨论】:

    • 这绝对是完美的!
    • 是的,但它不会显示点击发生的时间。
    【解决方案4】:

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

    使用纯 Java 是不可能的,因为 Java 只知道 Windows 上属于 Java 的 MouseEvents。

    【讨论】:

    • 有可能(见我的回答)。
    【解决方案5】:

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

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

    您需要包含native lib

    【讨论】:

    • 在 Jotschi(键盘/鼠标钩的原作者)的许可下,我几年前拿走了他的图书馆并创建了一个新版本。请查找最新版本和示例on GitHub。以及the blog post上的库描述。
    【解决方案6】:

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

    【讨论】:

    • @Chris:这是可能的,但有一些问题(见我的回答),因为并非每个版本的 Windows 都支持完全透明的 JFrame 等。然后另一个问题是在 OS X 上真正“完全透明” JFrame 不会拦截点击。
    【解决方案7】:

    有一个小技巧是可能的。应该是 100% 跨平台的(在 Linux 和 Windows 上测试)。基本上,您创建一个小的 JWindow,将其设置为“alwaysOnTop”,然后使用计时器用鼠标移动它。

    详情见我的回答here

    【讨论】:

      【解决方案8】:

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

      【讨论】:

        【解决方案9】:

        我还没有足够的代表离开 cmets,但这里是我的 cmets 关于其他技术的:

        • 使用本机库:可以,但有明显的分发限制

        • 使用 GlassPane 填充整个屏幕:GlassPanes 必须包含在 Window 中。

        • 创建一个包含桌面图片并填满整个屏幕的窗口:可以工作,但会突然使桌面静止。光标将不再变化,其他窗口或桌面中的任何动画或视频都将变得异常静止。

        替代解决方案: 如果您使用 Java 6u10 或更高版本,则屏幕填充窗口的改进是使窗口 completely transparent。将此窗口放在所有其他窗口的前面,并听取鼠标点击。它仍然有缺点,例如没有光标变化,但这取决于您要做什么。

        【讨论】:

        • 看起来 WizardOfOdds 对半透明窗口有同样的想法。请记住,它们的实现因平台而异,每个平台都有各种限制,正如他在 OSX 上演示的那样,实际上并不能保证在所有平台上都能正常工作。不过,看起来我们已经找到了一些不同的技术可以尝试。
        【解决方案10】:

        根据 SyntaxT3rr0r 的回答,我在 groovy 中创建了一个示例颜色选择器,展示了它是如何工作的。

        import java.awt.*
        import java.awt.datatransfer.*
        //import com.sun.awt.AWTUtilities;
        import javax.swing.WindowConstants as WC;
        import javax.swing.SwingConstants as SWC
        import groovy.swing.SwingBuilder
        
        class ColorPicker {
        
            SwingBuilder swb = new SwingBuilder()
            def window;
            def overlayWindow
            def mainPanel;
            def mainLabel;
            def menu;
            def transparent = new Color(0, 0, 0, 0);
            def nearlyTransparent = new Color(0, 0, 0, 26);
        
            Color color = new Color(150, 150, 255);
            def colorHex = { col ->
                col = col?: color;
                "#"+Integer.toHexString(col.getRGB())[2..-1]
            }
            def getTextColor = { baseColor ->
                baseColor = baseColor?: color;
                (baseColor.red*1.5 + baseColor.green*1.5 + baseColor.blue > 400) ? Color.BLACK : Color.WHITE;
            }
            def setDisplayColor = {newColor ->
                mainPanel.background = newColor
                mainLabel.foreground = getTextColor(newColor)
                mainLabel.text = colorHex(newColor)
            }
        
            def show(){
                menu = swb.popupMenu { // invoker: mainPanel
                    menuItem(text: "Pick Color", actionPerformed: capturePixelColor)
                    menuItem(text: "Copy to Clipboard", actionPerformed: {
                        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
                        clipboard.setContents(new StringSelection(colorHex()), null);
                    })
                    separator()
                    menuItem(text: "Close", actionPerformed: {dispose()})
                }
                window = swb.frame(
                    title: "Color Picker",
                    location:[50,50],
                    size:[60, 60],
                    resizable: false,
                    undecorated: true,
                    alwaysOnTop: true,
                    defaultCloseOperation:WC.EXIT_ON_CLOSE
                ){
                    def textColor = getTextColor()
                    mainPanel = panel( constraints: BorderLayout.CENTER,
                            border: lineBorder(color: Color.BLACK),
                            componentPopupMenu: menu){
                        borderLayout()
                        mainLabel = label(text: "--",
                            constraints: BorderLayout.CENTER,
                            horizontalAlignment: SWC.CENTER)
                    }
                }
                setDisplayColor(color);
                window.show();
            }
        
            def capturePixelColor = {
                def screenSize = Toolkit.getDefaultToolkit().screenSize
                overlayWindow = swb.frame(
                    location:[0,0],
                    size: screenSize,
                    resizable: false,
                    undecorated: true,
                    alwaysOnTop: true,
                    defaultCloseOperation:WC.DISPOSE_ON_CLOSE,
                    show: true,
                    background: nearlyTransparent, // AWTUtilities.setWindowOpacity(overlayWindow, 0.1f);
                    cursor: Cursor.CROSSHAIR_CURSOR,
                    mouseClicked: {event -> 
                        int x = event.getXOnScreen() // or maybe getX() is enough
                        int y = event.getYOnScreen()
                        overlayWindow.dispose()
                        overlayWindow = null
                        color = new Robot().getPixelColor(x, y)
                        setDisplayColor(color)
                    }
                )
            }
        
            public static void main(String...args){
                println "Welcome to ColorPicker"
                def picker = new ColorPicker()
                picker.show()
            }
        }
        

        【讨论】:

          【解决方案11】:

          看,我知道我迟到了 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);
          
                  }
              }
          }
          

          【讨论】:

            【解决方案12】:

            https://github.com/kwhat/jnativehookJNativeHook:全局 Java 键盘和鼠标监听器。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-04-14
              • 2018-10-27
              • 2011-12-09
              • 1970-01-01
              相关资源
              最近更新 更多