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

如何在Java中获取窗口外的鼠标点击坐标,java,swing,mouse,mouseclick-event,Java,Swing,Mouse,Mouseclick Event,我需要使用Swing实现一个类,它可以在用户单击屏幕上的任何位置时获取鼠标坐标。如果我想在自己的窗口内获取鼠标坐标,我会使用鼠标侦听器,但我希望它即使在用户在我的程序外单击时也能工作 我希望我的类的行为就像:用户点击下拉按钮,他可以点击屏幕上的任何地方来获得该点的颜色。但是我不知道使用纯Java是否可以做到这一点。我自己也没有尝试过,但也许你可以创建一个全屏、透明的面板/框架/等等,并在其中添加鼠标听筒 我不知道这是否可能 纯Java 使用纯Java是不可能的,因为Java只知道属于Java的W

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


我希望我的类的行为就像:用户点击下拉按钮,他可以点击屏幕上的任何地方来获得该点的颜色。但是我不知道使用纯Java是否可以做到这一点。

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

我不知道这是否可能 纯Java


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

这是可能的,尽管有限:

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

第二点在应用程序之外。

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

正如Keilly已经展示的,只可能获得鼠标位置


您需要包括一个

忘记
玻璃窗格
,另外还有一种100%纯Java的方法可以在OS X和Windows上实现

Java一直支持OS X上windows的半透明性,现在Java也支持windows上windows的半透明性(因为Java 1.6.0_10左右需要检查)

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

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

放弃无边界Java窗口

使用
Robot
getRgb(x,y)
就完成了

为什么要将alpha设置为10而不是0?因为Java不会截获点击,而是直接进入操作系统(至少在OSX上是这样)。有一个treshold,我知道它没有设置为“1”,也没有设置为“2”,大约是10左右


编辑我刚刚意识到你需要选择几种颜色,这比较棘手,但仍然可以使用100%Java完成。您可以使用“稍微不亮”的颜色(受“近乎透明”的“不可见”层的影响),或者在单击后必须删除该层,获得正确的像素颜色,然后再次放置“近乎透明”层。当然,这是一个骇客,但它可以在100%Java中完成。

我还没有足够的代表留下评论,但以下是我对其他技术的评论:

  • 使用本机库:将起作用,但有明显的分发限制

  • 使用玻璃窗格填充整个屏幕:玻璃窗格必须包含在窗口中

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

替代解决方案: 如果您使用的是Java6U10或更高版本,那么屏幕填充窗口的一个改进就是创建窗口。将此窗口放在所有其他窗口前面,并聆听鼠标单击。它仍然有缺点,例如没有光标更改,但这取决于您想做什么。

位置(x,y)和时间间隔 (d) 每次单击之间通过命令行参数提供。这是你的电话号码 节目

使用

p、 x和p.y将为您提供窗口外的坐标。

基于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()
    }
}

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


有关详细信息,请参阅我的答案。

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

        }
    }
}

JNativeHook:Java的全局键盘和鼠标侦听器。

这实际上相当聪明,但它当然只会报告应用程序外部的第一次单击,即导致焦点丢失的单击。之后,除非应用程序重新获得焦点,否则不会报告其他单击。现在,我想知道是否有可能对FOCUS_LOST事件做出反应,请求FOCUS。。。?!我不这么认为,focus请求或window.toFront()请求不能保证会影响VM之外的事情。在OSX上测试时,我发现这不起作用。另一个缺点是,您无法区分由于在窗口外单击和其他类型(例如,CTRL键切换到另一个正在运行的应用程序)而导致的焦点丢失。我已根据自己的情况调整了此解决方案,现在它满足了我的需要:-)@Chris:这是可能的,但确实存在
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()
    }
}
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);

        }
    }
}